1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694 |
- #
- # Parse tree nodes for expressions
- #
- from __future__ import absolute_import
- import cython
- cython.declare(error=object, warning=object, warn_once=object, InternalError=object,
- CompileError=object, UtilityCode=object, TempitaUtilityCode=object,
- StringEncoding=object, operator=object, local_errors=object, report_error=object,
- Naming=object, Nodes=object, PyrexTypes=object, py_object_type=object,
- list_type=object, tuple_type=object, set_type=object, dict_type=object,
- unicode_type=object, str_type=object, bytes_type=object, type_type=object,
- Builtin=object, Symtab=object, Utils=object, find_coercion_error=object,
- debug_disposal_code=object, debug_temp_alloc=object, debug_coercion=object,
- bytearray_type=object, slice_type=object, _py_int_types=object,
- IS_PYTHON3=cython.bint)
- import re
- import sys
- import copy
- import os.path
- import operator
- from .Errors import (
- error, warning, InternalError, CompileError, report_error, local_errors)
- from .Code import UtilityCode, TempitaUtilityCode
- from . import StringEncoding
- from . import Naming
- from . import Nodes
- from .Nodes import Node, utility_code_for_imports, analyse_type_annotation
- from . import PyrexTypes
- from .PyrexTypes import py_object_type, c_long_type, typecast, error_type, \
- unspecified_type
- from . import TypeSlots
- from .Builtin import list_type, tuple_type, set_type, dict_type, type_type, \
- unicode_type, str_type, bytes_type, bytearray_type, basestring_type, slice_type
- from . import Builtin
- from . import Symtab
- from .. import Utils
- from .Annotate import AnnotationItem
- from . import Future
- from ..Debugging import print_call_chain
- from .DebugFlags import debug_disposal_code, debug_temp_alloc, \
- debug_coercion
- from .Pythran import (to_pythran, is_pythran_supported_type, is_pythran_supported_operation_type,
- is_pythran_expr, pythran_func_type, pythran_binop_type, pythran_unaryop_type, has_np_pythran,
- pythran_indexing_code, pythran_indexing_type, is_pythran_supported_node_or_none, pythran_type,
- pythran_is_numpy_func_supported, pythran_get_func_include_file, pythran_functor)
- from .PyrexTypes import PythranExpr
- try:
- from __builtin__ import basestring
- except ImportError:
- # Python 3
- basestring = str
- any_string_type = (bytes, str)
- else:
- # Python 2
- any_string_type = (bytes, unicode)
- if sys.version_info[0] >= 3:
- IS_PYTHON3 = True
- _py_int_types = int
- else:
- IS_PYTHON3 = False
- _py_int_types = (int, long)
- class NotConstant(object):
- _obj = None
- def __new__(cls):
- if NotConstant._obj is None:
- NotConstant._obj = super(NotConstant, cls).__new__(cls)
- return NotConstant._obj
- def __repr__(self):
- return "<NOT CONSTANT>"
- not_a_constant = NotConstant()
- constant_value_not_set = object()
- # error messages when coercing from key[0] to key[1]
- coercion_error_dict = {
- # string related errors
- (unicode_type, str_type): ("Cannot convert Unicode string to 'str' implicitly."
- " This is not portable and requires explicit encoding."),
- (unicode_type, bytes_type): "Cannot convert Unicode string to 'bytes' implicitly, encoding required.",
- (unicode_type, PyrexTypes.c_char_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.",
- (unicode_type, PyrexTypes.c_const_char_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.",
- (unicode_type, PyrexTypes.c_uchar_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.",
- (unicode_type, PyrexTypes.c_const_uchar_ptr_type): "Unicode objects only support coercion to Py_UNICODE*.",
- (bytes_type, unicode_type): "Cannot convert 'bytes' object to unicode implicitly, decoding required",
- (bytes_type, str_type): "Cannot convert 'bytes' object to str implicitly. This is not portable to Py3.",
- (bytes_type, basestring_type): ("Cannot convert 'bytes' object to basestring implicitly."
- " This is not portable to Py3."),
- (bytes_type, PyrexTypes.c_py_unicode_ptr_type): "Cannot convert 'bytes' object to Py_UNICODE*, use 'unicode'.",
- (bytes_type, PyrexTypes.c_const_py_unicode_ptr_type): (
- "Cannot convert 'bytes' object to Py_UNICODE*, use 'unicode'."),
- (basestring_type, bytes_type): "Cannot convert 'basestring' object to bytes implicitly. This is not portable.",
- (str_type, unicode_type): ("str objects do not support coercion to unicode,"
- " use a unicode string literal instead (u'')"),
- (str_type, bytes_type): "Cannot convert 'str' to 'bytes' implicitly. This is not portable.",
- (str_type, PyrexTypes.c_char_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).",
- (str_type, PyrexTypes.c_const_char_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).",
- (str_type, PyrexTypes.c_uchar_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).",
- (str_type, PyrexTypes.c_const_uchar_ptr_type): "'str' objects do not support coercion to C types (use 'bytes'?).",
- (str_type, PyrexTypes.c_py_unicode_ptr_type): "'str' objects do not support coercion to C types (use 'unicode'?).",
- (str_type, PyrexTypes.c_const_py_unicode_ptr_type): (
- "'str' objects do not support coercion to C types (use 'unicode'?)."),
- (PyrexTypes.c_char_ptr_type, unicode_type): "Cannot convert 'char*' to unicode implicitly, decoding required",
- (PyrexTypes.c_const_char_ptr_type, unicode_type): (
- "Cannot convert 'char*' to unicode implicitly, decoding required"),
- (PyrexTypes.c_uchar_ptr_type, unicode_type): "Cannot convert 'char*' to unicode implicitly, decoding required",
- (PyrexTypes.c_const_uchar_ptr_type, unicode_type): (
- "Cannot convert 'char*' to unicode implicitly, decoding required"),
- }
- def find_coercion_error(type_tuple, default, env):
- err = coercion_error_dict.get(type_tuple)
- if err is None:
- return default
- elif (env.directives['c_string_encoding'] and
- any(t in type_tuple for t in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_uchar_ptr_type,
- PyrexTypes.c_const_char_ptr_type, PyrexTypes.c_const_uchar_ptr_type))):
- if type_tuple[1].is_pyobject:
- return default
- elif env.directives['c_string_encoding'] in ('ascii', 'default'):
- return default
- else:
- return "'%s' objects do not support coercion to C types with non-ascii or non-default c_string_encoding" % type_tuple[0].name
- else:
- return err
- def default_str_type(env):
- return {
- 'bytes': bytes_type,
- 'bytearray': bytearray_type,
- 'str': str_type,
- 'unicode': unicode_type
- }.get(env.directives['c_string_type'])
- def check_negative_indices(*nodes):
- """
- Raise a warning on nodes that are known to have negative numeric values.
- Used to find (potential) bugs inside of "wraparound=False" sections.
- """
- for node in nodes:
- if node is None or (
- not isinstance(node.constant_result, _py_int_types) and
- not isinstance(node.constant_result, float)):
- continue
- if node.constant_result < 0:
- warning(node.pos,
- "the result of using negative indices inside of "
- "code sections marked as 'wraparound=False' is "
- "undefined", level=1)
- def infer_sequence_item_type(env, seq_node, index_node=None, seq_type=None):
- if not seq_node.is_sequence_constructor:
- if seq_type is None:
- seq_type = seq_node.infer_type(env)
- if seq_type is tuple_type:
- # tuples are immutable => we can safely follow assignments
- if seq_node.cf_state and len(seq_node.cf_state) == 1:
- try:
- seq_node = seq_node.cf_state[0].rhs
- except AttributeError:
- pass
- if seq_node is not None and seq_node.is_sequence_constructor:
- if index_node is not None and index_node.has_constant_result():
- try:
- item = seq_node.args[index_node.constant_result]
- except (ValueError, TypeError, IndexError):
- pass
- else:
- return item.infer_type(env)
- # if we're lucky, all items have the same type
- item_types = set([item.infer_type(env) for item in seq_node.args])
- if len(item_types) == 1:
- return item_types.pop()
- return None
- def make_dedup_key(outer_type, item_nodes):
- """
- Recursively generate a deduplication key from a sequence of values.
- Includes Cython node types to work around the fact that (1, 2.0) == (1.0, 2), for example.
- @param outer_type: The type of the outer container.
- @param item_nodes: A sequence of constant nodes that will be traversed recursively.
- @return: A tuple that can be used as a dict key for deduplication.
- """
- item_keys = [
- (py_object_type, None, type(None)) if node is None
- # For sequences and their "mult_factor", see TupleNode.
- else make_dedup_key(node.type, [node.mult_factor if node.is_literal else None] + node.args) if node.is_sequence_constructor
- else make_dedup_key(node.type, (node.start, node.stop, node.step)) if node.is_slice
- # For constants, look at the Python value type if we don't know the concrete Cython type.
- else (node.type, node.constant_result,
- type(node.constant_result) if node.type is py_object_type else None) if node.has_constant_result()
- else None # something we cannot handle => short-circuit below
- for node in item_nodes
- ]
- if None in item_keys:
- return None
- return outer_type, tuple(item_keys)
- # Returns a block of code to translate the exception,
- # plus a boolean indicating whether to check for Python exceptions.
- def get_exception_handler(exception_value):
- if exception_value is None:
- return "__Pyx_CppExn2PyErr();", False
- elif (exception_value.type == PyrexTypes.c_char_type
- and exception_value.value == '*'):
- return "__Pyx_CppExn2PyErr();", True
- elif exception_value.type.is_pyobject:
- return (
- 'try { throw; } catch(const std::exception& exn) {'
- 'PyErr_SetString(%s, exn.what());'
- '} catch(...) { PyErr_SetNone(%s); }' % (
- exception_value.entry.cname,
- exception_value.entry.cname),
- False)
- else:
- return (
- '%s(); if (!PyErr_Occurred())'
- 'PyErr_SetString(PyExc_RuntimeError, '
- '"Error converting c++ exception.");' % (
- exception_value.entry.cname),
- False)
- def maybe_check_py_error(code, check_py_exception, pos, nogil):
- if check_py_exception:
- if nogil:
- code.putln(code.error_goto_if("__Pyx_ErrOccurredWithGIL()", pos))
- else:
- code.putln(code.error_goto_if("PyErr_Occurred()", pos))
- def translate_cpp_exception(code, pos, inside, py_result, exception_value, nogil):
- raise_py_exception, check_py_exception = get_exception_handler(exception_value)
- code.putln("try {")
- code.putln("%s" % inside)
- if py_result:
- code.putln(code.error_goto_if_null(py_result, pos))
- maybe_check_py_error(code, check_py_exception, pos, nogil)
- code.putln("} catch(...) {")
- if nogil:
- code.put_ensure_gil(declare_gilstate=True)
- code.putln(raise_py_exception)
- if nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(pos))
- code.putln("}")
- # Used to handle the case where an lvalue expression and an overloaded assignment
- # both have an exception declaration.
- def translate_double_cpp_exception(code, pos, lhs_type, lhs_code, rhs_code,
- lhs_exc_val, assign_exc_val, nogil):
- handle_lhs_exc, lhc_check_py_exc = get_exception_handler(lhs_exc_val)
- handle_assignment_exc, assignment_check_py_exc = get_exception_handler(assign_exc_val)
- code.putln("try {")
- code.putln(lhs_type.declaration_code("__pyx_local_lvalue = %s;" % lhs_code))
- maybe_check_py_error(code, lhc_check_py_exc, pos, nogil)
- code.putln("try {")
- code.putln("__pyx_local_lvalue = %s;" % rhs_code)
- maybe_check_py_error(code, assignment_check_py_exc, pos, nogil)
- # Catch any exception from the overloaded assignment.
- code.putln("} catch(...) {")
- if nogil:
- code.put_ensure_gil(declare_gilstate=True)
- code.putln(handle_assignment_exc)
- if nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(pos))
- code.putln("}")
- # Catch any exception from evaluating lhs.
- code.putln("} catch(...) {")
- if nogil:
- code.put_ensure_gil(declare_gilstate=True)
- code.putln(handle_lhs_exc)
- if nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(pos))
- code.putln('}')
- class ExprNode(Node):
- # subexprs [string] Class var holding names of subexpr node attrs
- # type PyrexType Type of the result
- # result_code string Code fragment
- # result_ctype string C type of result_code if different from type
- # is_temp boolean Result is in a temporary variable
- # is_sequence_constructor
- # boolean Is a list or tuple constructor expression
- # is_starred boolean Is a starred expression (e.g. '*a')
- # saved_subexpr_nodes
- # [ExprNode or [ExprNode or None] or None]
- # Cached result of subexpr_nodes()
- # use_managed_ref boolean use ref-counted temps/assignments/etc.
- # result_is_used boolean indicates that the result will be dropped and the
- # is_numpy_attribute boolean Is a Numpy module attribute
- # result_code/temp_result can safely be set to None
- # annotation ExprNode or None PEP526 annotation for names or expressions
- result_ctype = None
- type = None
- annotation = None
- temp_code = None
- old_temp = None # error checker for multiple frees etc.
- use_managed_ref = True # can be set by optimisation transforms
- result_is_used = True
- is_numpy_attribute = False
- # The Analyse Expressions phase for expressions is split
- # into two sub-phases:
- #
- # Analyse Types
- # Determines the result type of the expression based
- # on the types of its sub-expressions, and inserts
- # coercion nodes into the expression tree where needed.
- # Marks nodes which will need to have temporary variables
- # allocated.
- #
- # Allocate Temps
- # Allocates temporary variables where needed, and fills
- # in the result_code field of each node.
- #
- # ExprNode provides some convenience routines which
- # perform both of the above phases. These should only
- # be called from statement nodes, and only when no
- # coercion nodes need to be added around the expression
- # being analysed. In that case, the above two phases
- # should be invoked separately.
- #
- # Framework code in ExprNode provides much of the common
- # processing for the various phases. It makes use of the
- # 'subexprs' class attribute of ExprNodes, which should
- # contain a list of the names of attributes which can
- # hold sub-nodes or sequences of sub-nodes.
- #
- # The framework makes use of a number of abstract methods.
- # Their responsibilities are as follows.
- #
- # Declaration Analysis phase
- #
- # analyse_target_declaration
- # Called during the Analyse Declarations phase to analyse
- # the LHS of an assignment or argument of a del statement.
- # Nodes which cannot be the LHS of an assignment need not
- # implement it.
- #
- # Expression Analysis phase
- #
- # analyse_types
- # - Call analyse_types on all sub-expressions.
- # - Check operand types, and wrap coercion nodes around
- # sub-expressions where needed.
- # - Set the type of this node.
- # - If a temporary variable will be required for the
- # result, set the is_temp flag of this node.
- #
- # analyse_target_types
- # Called during the Analyse Types phase to analyse
- # the LHS of an assignment or argument of a del
- # statement. Similar responsibilities to analyse_types.
- #
- # target_code
- # Called by the default implementation of allocate_target_temps.
- # Should return a C lvalue for assigning to the node. The default
- # implementation calls calculate_result_code.
- #
- # check_const
- # - Check that this node and its subnodes form a
- # legal constant expression. If so, do nothing,
- # otherwise call not_const.
- #
- # The default implementation of check_const
- # assumes that the expression is not constant.
- #
- # check_const_addr
- # - Same as check_const, except check that the
- # expression is a C lvalue whose address is
- # constant. Otherwise, call addr_not_const.
- #
- # The default implementation of calc_const_addr
- # assumes that the expression is not a constant
- # lvalue.
- #
- # Code Generation phase
- #
- # generate_evaluation_code
- # - Call generate_evaluation_code for sub-expressions.
- # - Perform the functions of generate_result_code
- # (see below).
- # - If result is temporary, call generate_disposal_code
- # on all sub-expressions.
- #
- # A default implementation of generate_evaluation_code
- # is provided which uses the following abstract methods:
- #
- # generate_result_code
- # - Generate any C statements necessary to calculate
- # the result of this node from the results of its
- # sub-expressions.
- #
- # calculate_result_code
- # - Should return a C code fragment evaluating to the
- # result. This is only called when the result is not
- # a temporary.
- #
- # generate_assignment_code
- # Called on the LHS of an assignment.
- # - Call generate_evaluation_code for sub-expressions.
- # - Generate code to perform the assignment.
- # - If the assignment absorbed a reference, call
- # generate_post_assignment_code on the RHS,
- # otherwise call generate_disposal_code on it.
- #
- # generate_deletion_code
- # Called on an argument of a del statement.
- # - Call generate_evaluation_code for sub-expressions.
- # - Generate code to perform the deletion.
- # - Call generate_disposal_code on all sub-expressions.
- #
- #
- is_sequence_constructor = False
- is_dict_literal = False
- is_set_literal = False
- is_string_literal = False
- is_attribute = False
- is_subscript = False
- is_slice = False
- is_buffer_access = False
- is_memview_index = False
- is_memview_slice = False
- is_memview_broadcast = False
- is_memview_copy_assignment = False
- saved_subexpr_nodes = None
- is_temp = False
- is_target = False
- is_starred = False
- constant_result = constant_value_not_set
- child_attrs = property(fget=operator.attrgetter('subexprs'))
- def not_implemented(self, method_name):
- print_call_chain(method_name, "not implemented") ###
- raise InternalError(
- "%s.%s not implemented" %
- (self.__class__.__name__, method_name))
- def is_lvalue(self):
- return 0
- def is_addressable(self):
- return self.is_lvalue() and not self.type.is_memoryviewslice
- def is_ephemeral(self):
- # An ephemeral node is one whose result is in
- # a Python temporary and we suspect there are no
- # other references to it. Certain operations are
- # disallowed on such values, since they are
- # likely to result in a dangling pointer.
- return self.type.is_pyobject and self.is_temp
- def subexpr_nodes(self):
- # Extract a list of subexpression nodes based
- # on the contents of the subexprs class attribute.
- nodes = []
- for name in self.subexprs:
- item = getattr(self, name)
- if item is not None:
- if type(item) is list:
- nodes.extend(item)
- else:
- nodes.append(item)
- return nodes
- def result(self):
- if self.is_temp:
- #if not self.temp_code:
- # pos = (os.path.basename(self.pos[0].get_description()),) + self.pos[1:] if self.pos else '(?)'
- # raise RuntimeError("temp result name not set in %s at %r" % (
- # self.__class__.__name__, pos))
- return self.temp_code
- else:
- return self.calculate_result_code()
- def pythran_result(self, type_=None):
- if is_pythran_supported_node_or_none(self):
- return to_pythran(self)
- assert(type_ is not None)
- return to_pythran(self, type_)
- def is_c_result_required(self):
- """
- Subtypes may return False here if result temp allocation can be skipped.
- """
- return True
- def result_as(self, type = None):
- # Return the result code cast to the specified C type.
- if (self.is_temp and self.type.is_pyobject and
- type != py_object_type):
- # Allocated temporaries are always PyObject *, which may not
- # reflect the actual type (e.g. an extension type)
- return typecast(type, py_object_type, self.result())
- return typecast(type, self.ctype(), self.result())
- def py_result(self):
- # Return the result code cast to PyObject *.
- return self.result_as(py_object_type)
- def ctype(self):
- # Return the native C type of the result (i.e. the
- # C type of the result_code expression).
- return self.result_ctype or self.type
- def get_constant_c_result_code(self):
- # Return the constant value of this node as a result code
- # string, or None if the node is not constant. This method
- # can be called when the constant result code is required
- # before the code generation phase.
- #
- # The return value is a string that can represent a simple C
- # value, a constant C name or a constant C expression. If the
- # node type depends on Python code, this must return None.
- return None
- def calculate_constant_result(self):
- # Calculate the constant compile time result value of this
- # expression and store it in ``self.constant_result``. Does
- # nothing by default, thus leaving ``self.constant_result``
- # unknown. If valid, the result can be an arbitrary Python
- # value.
- #
- # This must only be called when it is assured that all
- # sub-expressions have a valid constant_result value. The
- # ConstantFolding transform will do this.
- pass
- def has_constant_result(self):
- return self.constant_result is not constant_value_not_set and \
- self.constant_result is not not_a_constant
- def compile_time_value(self, denv):
- # Return value of compile-time expression, or report error.
- error(self.pos, "Invalid compile-time expression")
- def compile_time_value_error(self, e):
- error(self.pos, "Error in compile-time expression: %s: %s" % (
- e.__class__.__name__, e))
- # ------------- Declaration Analysis ----------------
- def analyse_target_declaration(self, env):
- error(self.pos, "Cannot assign to or delete this")
- # ------------- Expression Analysis ----------------
- def analyse_const_expression(self, env):
- # Called during the analyse_declarations phase of a
- # constant expression. Analyses the expression's type,
- # checks whether it is a legal const expression,
- # and determines its value.
- node = self.analyse_types(env)
- node.check_const()
- return node
- def analyse_expressions(self, env):
- # Convenience routine performing both the Type
- # Analysis and Temp Allocation phases for a whole
- # expression.
- return self.analyse_types(env)
- def analyse_target_expression(self, env, rhs):
- # Convenience routine performing both the Type
- # Analysis and Temp Allocation phases for the LHS of
- # an assignment.
- return self.analyse_target_types(env)
- def analyse_boolean_expression(self, env):
- # Analyse expression and coerce to a boolean.
- node = self.analyse_types(env)
- bool = node.coerce_to_boolean(env)
- return bool
- def analyse_temp_boolean_expression(self, env):
- # Analyse boolean expression and coerce result into
- # a temporary. This is used when a branch is to be
- # performed on the result and we won't have an
- # opportunity to ensure disposal code is executed
- # afterwards. By forcing the result into a temporary,
- # we ensure that all disposal has been done by the
- # time we get the result.
- node = self.analyse_types(env)
- return node.coerce_to_boolean(env).coerce_to_simple(env)
- # --------------- Type Inference -----------------
- def type_dependencies(self, env):
- # Returns the list of entries whose types must be determined
- # before the type of self can be inferred.
- if hasattr(self, 'type') and self.type is not None:
- return ()
- return sum([node.type_dependencies(env) for node in self.subexpr_nodes()], ())
- def infer_type(self, env):
- # Attempt to deduce the type of self.
- # Differs from analyse_types as it avoids unnecessary
- # analysis of subexpressions, but can assume everything
- # in self.type_dependencies() has been resolved.
- if hasattr(self, 'type') and self.type is not None:
- return self.type
- elif hasattr(self, 'entry') and self.entry is not None:
- return self.entry.type
- else:
- self.not_implemented("infer_type")
- def nonlocally_immutable(self):
- # Returns whether this variable is a safe reference, i.e.
- # can't be modified as part of globals or closures.
- return self.is_literal or self.is_temp or self.type.is_array or self.type.is_cfunction
- def inferable_item_node(self, index=0):
- """
- Return a node that represents the (type) result of an indexing operation,
- e.g. for tuple unpacking or iteration.
- """
- return IndexNode(self.pos, base=self, index=IntNode(
- self.pos, value=str(index), constant_result=index, type=PyrexTypes.c_py_ssize_t_type))
- # --------------- Type Analysis ------------------
- def analyse_as_module(self, env):
- # If this node can be interpreted as a reference to a
- # cimported module, return its scope, else None.
- return None
- def analyse_as_type(self, env):
- # If this node can be interpreted as a reference to a
- # type, return that type, else None.
- return None
- def analyse_as_extension_type(self, env):
- # If this node can be interpreted as a reference to an
- # extension type or builtin type, return its type, else None.
- return None
- def analyse_types(self, env):
- self.not_implemented("analyse_types")
- def analyse_target_types(self, env):
- return self.analyse_types(env)
- def nogil_check(self, env):
- # By default, any expression based on Python objects is
- # prevented in nogil environments. Subtypes must override
- # this if they can work without the GIL.
- if self.type and self.type.is_pyobject:
- self.gil_error()
- def gil_assignment_check(self, env):
- if env.nogil and self.type.is_pyobject:
- error(self.pos, "Assignment of Python object not allowed without gil")
- def check_const(self):
- self.not_const()
- return False
- def not_const(self):
- error(self.pos, "Not allowed in a constant expression")
- def check_const_addr(self):
- self.addr_not_const()
- return False
- def addr_not_const(self):
- error(self.pos, "Address is not constant")
- # ----------------- Result Allocation -----------------
- def result_in_temp(self):
- # Return true if result is in a temporary owned by
- # this node or one of its subexpressions. Overridden
- # by certain nodes which can share the result of
- # a subnode.
- return self.is_temp
- def target_code(self):
- # Return code fragment for use as LHS of a C assignment.
- return self.calculate_result_code()
- def calculate_result_code(self):
- self.not_implemented("calculate_result_code")
- # def release_target_temp(self, env):
- # # Release temporaries used by LHS of an assignment.
- # self.release_subexpr_temps(env)
- def allocate_temp_result(self, code):
- if self.temp_code:
- raise RuntimeError("Temp allocated multiple times in %r: %r" % (self.__class__.__name__, self.pos))
- type = self.type
- if not type.is_void:
- if type.is_pyobject:
- type = PyrexTypes.py_object_type
- elif not (self.result_is_used or type.is_memoryviewslice or self.is_c_result_required()):
- self.temp_code = None
- return
- self.temp_code = code.funcstate.allocate_temp(
- type, manage_ref=self.use_managed_ref)
- else:
- self.temp_code = None
- def release_temp_result(self, code):
- if not self.temp_code:
- if not self.result_is_used:
- # not used anyway, so ignore if not set up
- return
- pos = (os.path.basename(self.pos[0].get_description()),) + self.pos[1:] if self.pos else '(?)'
- if self.old_temp:
- raise RuntimeError("temp %s released multiple times in %s at %r" % (
- self.old_temp, self.__class__.__name__, pos))
- else:
- raise RuntimeError("no temp, but release requested in %s at %r" % (
- self.__class__.__name__, pos))
- code.funcstate.release_temp(self.temp_code)
- self.old_temp = self.temp_code
- self.temp_code = None
- # ---------------- Code Generation -----------------
- def make_owned_reference(self, code):
- """
- If result is a pyobject, make sure we own a reference to it.
- If the result is in a temp, it is already a new reference.
- """
- if self.type.is_pyobject and not self.result_in_temp():
- code.put_incref(self.result(), self.ctype())
- def make_owned_memoryviewslice(self, code):
- """
- Make sure we own the reference to this memoryview slice.
- """
- if not self.result_in_temp():
- code.put_incref_memoryviewslice(self.result(),
- have_gil=self.in_nogil_context)
- def generate_evaluation_code(self, code):
- # Generate code to evaluate this node and
- # its sub-expressions, and dispose of any
- # temporary results of its sub-expressions.
- self.generate_subexpr_evaluation_code(code)
- code.mark_pos(self.pos)
- if self.is_temp:
- self.allocate_temp_result(code)
- self.generate_result_code(code)
- if self.is_temp and not (self.type.is_string or self.type.is_pyunicode_ptr):
- # If we are temp we do not need to wait until this node is disposed
- # before disposing children.
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- def generate_subexpr_evaluation_code(self, code):
- for node in self.subexpr_nodes():
- node.generate_evaluation_code(code)
- def generate_result_code(self, code):
- self.not_implemented("generate_result_code")
- def generate_disposal_code(self, code):
- if self.is_temp:
- if self.type.is_string or self.type.is_pyunicode_ptr:
- # postponed from self.generate_evaluation_code()
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- if self.result():
- if self.type.is_pyobject:
- code.put_decref_clear(self.result(), self.ctype())
- elif self.type.is_memoryviewslice:
- code.put_xdecref_memoryviewslice(
- self.result(), have_gil=not self.in_nogil_context)
- code.putln("%s.memview = NULL;" % self.result())
- code.putln("%s.data = NULL;" % self.result())
- else:
- # Already done if self.is_temp
- self.generate_subexpr_disposal_code(code)
- def generate_subexpr_disposal_code(self, code):
- # Generate code to dispose of temporary results
- # of all sub-expressions.
- for node in self.subexpr_nodes():
- node.generate_disposal_code(code)
- def generate_post_assignment_code(self, code):
- if self.is_temp:
- if self.type.is_string or self.type.is_pyunicode_ptr:
- # postponed from self.generate_evaluation_code()
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- elif self.type.is_pyobject:
- code.putln("%s = 0;" % self.result())
- elif self.type.is_memoryviewslice:
- code.putln("%s.memview = NULL;" % self.result())
- code.putln("%s.data = NULL;" % self.result())
- else:
- self.generate_subexpr_disposal_code(code)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- # Stub method for nodes which are not legal as
- # the LHS of an assignment. An error will have
- # been reported earlier.
- pass
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- # Stub method for nodes that are not legal as
- # the argument of a del statement. An error
- # will have been reported earlier.
- pass
- def free_temps(self, code):
- if self.is_temp:
- if not self.type.is_void:
- self.release_temp_result(code)
- else:
- self.free_subexpr_temps(code)
- def free_subexpr_temps(self, code):
- for sub in self.subexpr_nodes():
- sub.free_temps(code)
- def generate_function_definitions(self, env, code):
- pass
- # ---------------- Annotation ---------------------
- def annotate(self, code):
- for node in self.subexpr_nodes():
- node.annotate(code)
- # ----------------- Coercion ----------------------
- def coerce_to(self, dst_type, env):
- # Coerce the result so that it can be assigned to
- # something of type dst_type. If processing is necessary,
- # wraps this node in a coercion node and returns that.
- # Otherwise, returns this node unchanged.
- #
- # This method is called during the analyse_expressions
- # phase of the src_node's processing.
- #
- # Note that subclasses that override this (especially
- # ConstNodes) must not (re-)set their own .type attribute
- # here. Since expression nodes may turn up in different
- # places in the tree (e.g. inside of CloneNodes in cascaded
- # assignments), this method must return a new node instance
- # if it changes the type.
- #
- src = self
- src_type = self.type
- if self.check_for_coercion_error(dst_type, env):
- return self
- used_as_reference = dst_type.is_reference
- if used_as_reference and not src_type.is_reference:
- dst_type = dst_type.ref_base_type
- if src_type.is_const:
- src_type = src_type.const_base_type
- if src_type.is_fused or dst_type.is_fused:
- # See if we are coercing a fused function to a pointer to a
- # specialized function
- if (src_type.is_cfunction and not dst_type.is_fused and
- dst_type.is_ptr and dst_type.base_type.is_cfunction):
- dst_type = dst_type.base_type
- for signature in src_type.get_all_specialized_function_types():
- if signature.same_as(dst_type):
- src.type = signature
- src.entry = src.type.entry
- src.entry.used = True
- return self
- if src_type.is_fused:
- error(self.pos, "Type is not specialized")
- elif src_type.is_null_ptr and dst_type.is_ptr:
- # NULL can be implicitly cast to any pointer type
- return self
- else:
- error(self.pos, "Cannot coerce to a type that is not specialized")
- self.type = error_type
- return self
- if self.coercion_type is not None:
- # This is purely for error checking purposes!
- node = NameNode(self.pos, name='', type=self.coercion_type)
- node.coerce_to(dst_type, env)
- if dst_type.is_memoryviewslice:
- from . import MemoryView
- if not src.type.is_memoryviewslice:
- if src.type.is_pyobject:
- src = CoerceToMemViewSliceNode(src, dst_type, env)
- elif src.type.is_array:
- src = CythonArrayNode.from_carray(src, env).coerce_to(dst_type, env)
- elif not src_type.is_error:
- error(self.pos,
- "Cannot convert '%s' to memoryviewslice" % (src_type,))
- else:
- if src.type.writable_needed:
- dst_type.writable_needed = True
- if not src.type.conforms_to(dst_type, broadcast=self.is_memview_broadcast,
- copying=self.is_memview_copy_assignment):
- if src.type.dtype.same_as(dst_type.dtype):
- msg = "Memoryview '%s' not conformable to memoryview '%s'."
- tup = src.type, dst_type
- else:
- msg = "Different base types for memoryviews (%s, %s)"
- tup = src.type.dtype, dst_type.dtype
- error(self.pos, msg % tup)
- elif dst_type.is_pyobject:
- if not src.type.is_pyobject:
- if dst_type is bytes_type and src.type.is_int:
- src = CoerceIntToBytesNode(src, env)
- else:
- src = CoerceToPyTypeNode(src, env, type=dst_type)
- if not src.type.subtype_of(dst_type):
- if src.constant_result is not None:
- src = PyTypeTestNode(src, dst_type, env)
- elif is_pythran_expr(dst_type) and is_pythran_supported_type(src.type):
- # We let the compiler decide whether this is valid
- return src
- elif is_pythran_expr(src.type):
- if is_pythran_supported_type(dst_type):
- # Match the case were a pythran expr is assigned to a value, or vice versa.
- # We let the C++ compiler decide whether this is valid or not!
- return src
- # Else, we need to convert the Pythran expression to a Python object
- src = CoerceToPyTypeNode(src, env, type=dst_type)
- elif src.type.is_pyobject:
- if used_as_reference and dst_type.is_cpp_class:
- warning(
- self.pos,
- "Cannot pass Python object as C++ data structure reference (%s &), will pass by copy." % dst_type)
- src = CoerceFromPyTypeNode(dst_type, src, env)
- elif (dst_type.is_complex
- and src_type != dst_type
- and dst_type.assignable_from(src_type)):
- src = CoerceToComplexNode(src, dst_type, env)
- else: # neither src nor dst are py types
- # Added the string comparison, since for c types that
- # is enough, but Cython gets confused when the types are
- # in different pxi files.
- # TODO: Remove this hack and require shared declarations.
- if not (src.type == dst_type or str(src.type) == str(dst_type) or dst_type.assignable_from(src_type)):
- self.fail_assignment(dst_type)
- return src
- def fail_assignment(self, dst_type):
- error(self.pos, "Cannot assign type '%s' to '%s'" % (self.type, dst_type))
- def check_for_coercion_error(self, dst_type, env, fail=False, default=None):
- if fail and not default:
- default = "Cannot assign type '%(FROM)s' to '%(TO)s'"
- message = find_coercion_error((self.type, dst_type), default, env)
- if message is not None:
- error(self.pos, message % {'FROM': self.type, 'TO': dst_type})
- return True
- if fail:
- self.fail_assignment(dst_type)
- return True
- return False
- def coerce_to_pyobject(self, env):
- return self.coerce_to(PyrexTypes.py_object_type, env)
- def coerce_to_boolean(self, env):
- # Coerce result to something acceptable as
- # a boolean value.
- # if it's constant, calculate the result now
- if self.has_constant_result():
- bool_value = bool(self.constant_result)
- return BoolNode(self.pos, value=bool_value,
- constant_result=bool_value)
- type = self.type
- if type.is_enum or type.is_error:
- return self
- elif type.is_pyobject or type.is_int or type.is_ptr or type.is_float:
- return CoerceToBooleanNode(self, env)
- elif type.is_cpp_class and type.scope and type.scope.lookup("operator bool"):
- return SimpleCallNode(
- self.pos,
- function=AttributeNode(
- self.pos, obj=self, attribute=StringEncoding.EncodedString('operator bool')),
- args=[]).analyse_types(env)
- elif type.is_ctuple:
- bool_value = len(type.components) == 0
- return BoolNode(self.pos, value=bool_value,
- constant_result=bool_value)
- else:
- error(self.pos, "Type '%s' not acceptable as a boolean" % type)
- return self
- def coerce_to_integer(self, env):
- # If not already some C integer type, coerce to longint.
- if self.type.is_int:
- return self
- else:
- return self.coerce_to(PyrexTypes.c_long_type, env)
- def coerce_to_temp(self, env):
- # Ensure that the result is in a temporary.
- if self.result_in_temp():
- return self
- else:
- return CoerceToTempNode(self, env)
- def coerce_to_simple(self, env):
- # Ensure that the result is simple (see is_simple).
- if self.is_simple():
- return self
- else:
- return self.coerce_to_temp(env)
- def is_simple(self):
- # A node is simple if its result is something that can
- # be referred to without performing any operations, e.g.
- # a constant, local var, C global var, struct member
- # reference, or temporary.
- return self.result_in_temp()
- def may_be_none(self):
- if self.type and not (self.type.is_pyobject or
- self.type.is_memoryviewslice):
- return False
- if self.has_constant_result():
- return self.constant_result is not None
- return True
- def as_cython_attribute(self):
- return None
- def as_none_safe_node(self, message, error="PyExc_TypeError", format_args=()):
- # Wraps the node in a NoneCheckNode if it is not known to be
- # not-None (e.g. because it is a Python literal).
- if self.may_be_none():
- return NoneCheckNode(self, error, message, format_args)
- else:
- return self
- @classmethod
- def from_node(cls, node, **kwargs):
- """Instantiate this node class from another node, properly
- copying over all attributes that one would forget otherwise.
- """
- attributes = "cf_state cf_maybe_null cf_is_null constant_result".split()
- for attr_name in attributes:
- if attr_name in kwargs:
- continue
- try:
- value = getattr(node, attr_name)
- except AttributeError:
- pass
- else:
- kwargs[attr_name] = value
- return cls(node.pos, **kwargs)
- class AtomicExprNode(ExprNode):
- # Abstract base class for expression nodes which have
- # no sub-expressions.
- subexprs = []
- # Override to optimize -- we know we have no children
- def generate_subexpr_evaluation_code(self, code):
- pass
- def generate_subexpr_disposal_code(self, code):
- pass
- class PyConstNode(AtomicExprNode):
- # Abstract base class for constant Python values.
- is_literal = 1
- type = py_object_type
- def is_simple(self):
- return 1
- def may_be_none(self):
- return False
- def analyse_types(self, env):
- return self
- def calculate_result_code(self):
- return self.value
- def generate_result_code(self, code):
- pass
- class NoneNode(PyConstNode):
- # The constant value None
- is_none = 1
- value = "Py_None"
- constant_result = None
- nogil_check = None
- def compile_time_value(self, denv):
- return None
- def may_be_none(self):
- return True
- def coerce_to(self, dst_type, env):
- if not (dst_type.is_pyobject or dst_type.is_memoryviewslice or dst_type.is_error):
- # Catch this error early and loudly.
- error(self.pos, "Cannot assign None to %s" % dst_type)
- return super(NoneNode, self).coerce_to(dst_type, env)
- class EllipsisNode(PyConstNode):
- # '...' in a subscript list.
- value = "Py_Ellipsis"
- constant_result = Ellipsis
- def compile_time_value(self, denv):
- return Ellipsis
- class ConstNode(AtomicExprNode):
- # Abstract base type for literal constant nodes.
- #
- # value string C code fragment
- is_literal = 1
- nogil_check = None
- def is_simple(self):
- return 1
- def nonlocally_immutable(self):
- return 1
- def may_be_none(self):
- return False
- def analyse_types(self, env):
- return self # Types are held in class variables
- def check_const(self):
- return True
- def get_constant_c_result_code(self):
- return self.calculate_result_code()
- def calculate_result_code(self):
- return str(self.value)
- def generate_result_code(self, code):
- pass
- class BoolNode(ConstNode):
- type = PyrexTypes.c_bint_type
- # The constant value True or False
- def calculate_constant_result(self):
- self.constant_result = self.value
- def compile_time_value(self, denv):
- return self.value
- def calculate_result_code(self):
- if self.type.is_pyobject:
- return self.value and 'Py_True' or 'Py_False'
- else:
- return str(int(self.value))
- def coerce_to(self, dst_type, env):
- if dst_type == self.type:
- return self
- if dst_type is py_object_type and self.type is Builtin.bool_type:
- return self
- if dst_type.is_pyobject and self.type.is_int:
- return BoolNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=Builtin.bool_type)
- if dst_type.is_int and self.type.is_pyobject:
- return BoolNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=PyrexTypes.c_bint_type)
- return ConstNode.coerce_to(self, dst_type, env)
- class NullNode(ConstNode):
- type = PyrexTypes.c_null_ptr_type
- value = "NULL"
- constant_result = 0
- def get_constant_c_result_code(self):
- return self.value
- class CharNode(ConstNode):
- type = PyrexTypes.c_char_type
- def calculate_constant_result(self):
- self.constant_result = ord(self.value)
- def compile_time_value(self, denv):
- return ord(self.value)
- def calculate_result_code(self):
- return "'%s'" % StringEncoding.escape_char(self.value)
- class IntNode(ConstNode):
- # unsigned "" or "U"
- # longness "" or "L" or "LL"
- # is_c_literal True/False/None creator considers this a C integer literal
- unsigned = ""
- longness = ""
- is_c_literal = None # unknown
- def __init__(self, pos, **kwds):
- ExprNode.__init__(self, pos, **kwds)
- if 'type' not in kwds:
- self.type = self.find_suitable_type_for_value()
- def find_suitable_type_for_value(self):
- if self.constant_result is constant_value_not_set:
- try:
- self.calculate_constant_result()
- except ValueError:
- pass
- # we ignore 'is_c_literal = True' and instead map signed 32bit
- # integers as C long values
- if self.is_c_literal or \
- not self.has_constant_result() or \
- self.unsigned or self.longness == 'LL':
- # clearly a C literal
- rank = (self.longness == 'LL') and 2 or 1
- suitable_type = PyrexTypes.modifiers_and_name_to_type[not self.unsigned, rank, "int"]
- if self.type:
- suitable_type = PyrexTypes.widest_numeric_type(suitable_type, self.type)
- else:
- # C literal or Python literal - split at 32bit boundary
- if -2**31 <= self.constant_result < 2**31:
- if self.type and self.type.is_int:
- suitable_type = self.type
- else:
- suitable_type = PyrexTypes.c_long_type
- else:
- suitable_type = PyrexTypes.py_object_type
- return suitable_type
- def coerce_to(self, dst_type, env):
- if self.type is dst_type:
- return self
- elif dst_type.is_float:
- if self.has_constant_result():
- return FloatNode(self.pos, value='%d.0' % int(self.constant_result), type=dst_type,
- constant_result=float(self.constant_result))
- else:
- return FloatNode(self.pos, value=self.value, type=dst_type,
- constant_result=not_a_constant)
- if dst_type.is_numeric and not dst_type.is_complex:
- node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
- type=dst_type, is_c_literal=True,
- unsigned=self.unsigned, longness=self.longness)
- return node
- elif dst_type.is_pyobject:
- node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
- type=PyrexTypes.py_object_type, is_c_literal=False,
- unsigned=self.unsigned, longness=self.longness)
- else:
- # FIXME: not setting the type here to keep it working with
- # complex numbers. Should they be special cased?
- node = IntNode(self.pos, value=self.value, constant_result=self.constant_result,
- unsigned=self.unsigned, longness=self.longness)
- # We still need to perform normal coerce_to processing on the
- # result, because we might be coercing to an extension type,
- # in which case a type test node will be needed.
- return ConstNode.coerce_to(node, dst_type, env)
- def coerce_to_boolean(self, env):
- return IntNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=PyrexTypes.c_bint_type,
- unsigned=self.unsigned, longness=self.longness)
- def generate_evaluation_code(self, code):
- if self.type.is_pyobject:
- # pre-allocate a Python version of the number
- plain_integer_string = str(Utils.str_to_number(self.value))
- self.result_code = code.get_py_int(plain_integer_string, self.longness)
- else:
- self.result_code = self.get_constant_c_result_code()
- def get_constant_c_result_code(self):
- unsigned, longness = self.unsigned, self.longness
- literal = self.value_as_c_integer_string()
- if not (unsigned or longness) and self.type.is_int and literal[0] == '-' and literal[1] != '0':
- # negative decimal literal => guess longness from type to prevent wrap-around
- if self.type.rank >= PyrexTypes.c_longlong_type.rank:
- longness = 'LL'
- elif self.type.rank >= PyrexTypes.c_long_type.rank:
- longness = 'L'
- return literal + unsigned + longness
- def value_as_c_integer_string(self):
- value = self.value
- if len(value) <= 2:
- # too short to go wrong (and simplifies code below)
- return value
- neg_sign = ''
- if value[0] == '-':
- neg_sign = '-'
- value = value[1:]
- if value[0] == '0':
- literal_type = value[1] # 0'o' - 0'b' - 0'x'
- # 0x123 hex literals and 0123 octal literals work nicely in C
- # but C-incompatible Py3 oct/bin notations need conversion
- if neg_sign and literal_type in 'oOxX0123456789' and value[2:].isdigit():
- # negative hex/octal literal => prevent C compiler from using
- # unsigned integer types by converting to decimal (see C standard 6.4.4.1)
- value = str(Utils.str_to_number(value))
- elif literal_type in 'oO':
- value = '0' + value[2:] # '0o123' => '0123'
- elif literal_type in 'bB':
- value = str(int(value[2:], 2))
- elif value.isdigit() and not self.unsigned and not self.longness:
- if not neg_sign:
- # C compilers do not consider unsigned types for decimal literals,
- # but they do for hex (see C standard 6.4.4.1)
- value = '0x%X' % int(value)
- return neg_sign + value
- def calculate_result_code(self):
- return self.result_code
- def calculate_constant_result(self):
- self.constant_result = Utils.str_to_number(self.value)
- def compile_time_value(self, denv):
- return Utils.str_to_number(self.value)
- class FloatNode(ConstNode):
- type = PyrexTypes.c_double_type
- def calculate_constant_result(self):
- self.constant_result = float(self.value)
- def compile_time_value(self, denv):
- return float(self.value)
- def coerce_to(self, dst_type, env):
- if dst_type.is_pyobject and self.type.is_float:
- return FloatNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=Builtin.float_type)
- if dst_type.is_float and self.type.is_pyobject:
- return FloatNode(
- self.pos, value=self.value,
- constant_result=self.constant_result,
- type=dst_type)
- return ConstNode.coerce_to(self, dst_type, env)
- def calculate_result_code(self):
- return self.result_code
- def get_constant_c_result_code(self):
- strval = self.value
- assert isinstance(strval, basestring)
- cmpval = repr(float(strval))
- if cmpval == 'nan':
- return "(Py_HUGE_VAL * 0)"
- elif cmpval == 'inf':
- return "Py_HUGE_VAL"
- elif cmpval == '-inf':
- return "(-Py_HUGE_VAL)"
- else:
- return strval
- def generate_evaluation_code(self, code):
- c_value = self.get_constant_c_result_code()
- if self.type.is_pyobject:
- self.result_code = code.get_py_float(self.value, c_value)
- else:
- self.result_code = c_value
- def _analyse_name_as_type(name, pos, env):
- type = PyrexTypes.parse_basic_type(name)
- if type is not None:
- return type
- global_entry = env.global_scope().lookup(name)
- if global_entry and global_entry.type and (
- global_entry.type.is_extension_type
- or global_entry.type.is_struct_or_union
- or global_entry.type.is_builtin_type
- or global_entry.type.is_cpp_class):
- return global_entry.type
- from .TreeFragment import TreeFragment
- with local_errors(ignore=True):
- pos = (pos[0], pos[1], pos[2]-7)
- try:
- declaration = TreeFragment(u"sizeof(%s)" % name, name=pos[0].filename, initial_pos=pos)
- except CompileError:
- pass
- else:
- sizeof_node = declaration.root.stats[0].expr
- if isinstance(sizeof_node, SizeofTypeNode):
- sizeof_node = sizeof_node.analyse_types(env)
- if isinstance(sizeof_node, SizeofTypeNode):
- return sizeof_node.arg_type
- return None
- class BytesNode(ConstNode):
- # A char* or bytes literal
- #
- # value BytesLiteral
- is_string_literal = True
- # start off as Python 'bytes' to support len() in O(1)
- type = bytes_type
- def calculate_constant_result(self):
- self.constant_result = self.value
- def as_sliced_node(self, start, stop, step=None):
- value = StringEncoding.bytes_literal(self.value[start:stop:step], self.value.encoding)
- return BytesNode(self.pos, value=value, constant_result=value)
- def compile_time_value(self, denv):
- return self.value.byteencode()
- def analyse_as_type(self, env):
- return _analyse_name_as_type(self.value.decode('ISO8859-1'), self.pos, env)
- def can_coerce_to_char_literal(self):
- return len(self.value) == 1
- def coerce_to_boolean(self, env):
- # This is special because testing a C char* for truth directly
- # would yield the wrong result.
- bool_value = bool(self.value)
- return BoolNode(self.pos, value=bool_value, constant_result=bool_value)
- def coerce_to(self, dst_type, env):
- if self.type == dst_type:
- return self
- if dst_type.is_int:
- if not self.can_coerce_to_char_literal():
- error(self.pos, "Only single-character string literals can be coerced into ints.")
- return self
- if dst_type.is_unicode_char:
- error(self.pos, "Bytes literals cannot coerce to Py_UNICODE/Py_UCS4, use a unicode literal instead.")
- return self
- return CharNode(self.pos, value=self.value,
- constant_result=ord(self.value))
- node = BytesNode(self.pos, value=self.value, constant_result=self.constant_result)
- if dst_type.is_pyobject:
- if dst_type in (py_object_type, Builtin.bytes_type):
- node.type = Builtin.bytes_type
- else:
- self.check_for_coercion_error(dst_type, env, fail=True)
- return node
- elif dst_type in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_const_char_ptr_type):
- node.type = dst_type
- return node
- elif dst_type in (PyrexTypes.c_uchar_ptr_type, PyrexTypes.c_const_uchar_ptr_type, PyrexTypes.c_void_ptr_type):
- node.type = (PyrexTypes.c_const_char_ptr_type if dst_type == PyrexTypes.c_const_uchar_ptr_type
- else PyrexTypes.c_char_ptr_type)
- return CastNode(node, dst_type)
- elif dst_type.assignable_from(PyrexTypes.c_char_ptr_type):
- # Exclude the case of passing a C string literal into a non-const C++ string.
- if not dst_type.is_cpp_class or dst_type.is_const:
- node.type = dst_type
- return node
- # We still need to perform normal coerce_to processing on the
- # result, because we might be coercing to an extension type,
- # in which case a type test node will be needed.
- return ConstNode.coerce_to(node, dst_type, env)
- def generate_evaluation_code(self, code):
- if self.type.is_pyobject:
- result = code.get_py_string_const(self.value)
- elif self.type.is_const:
- result = code.get_string_const(self.value)
- else:
- # not const => use plain C string literal and cast to mutable type
- literal = self.value.as_c_string_literal()
- # C++ may require a cast
- result = typecast(self.type, PyrexTypes.c_void_ptr_type, literal)
- self.result_code = result
- def get_constant_c_result_code(self):
- return None # FIXME
- def calculate_result_code(self):
- return self.result_code
- class UnicodeNode(ConstNode):
- # A Py_UNICODE* or unicode literal
- #
- # value EncodedString
- # bytes_value BytesLiteral the literal parsed as bytes string
- # ('-3' unicode literals only)
- is_string_literal = True
- bytes_value = None
- type = unicode_type
- def calculate_constant_result(self):
- self.constant_result = self.value
- def analyse_as_type(self, env):
- return _analyse_name_as_type(self.value, self.pos, env)
- def as_sliced_node(self, start, stop, step=None):
- if StringEncoding.string_contains_surrogates(self.value[:stop]):
- # this is unsafe as it may give different results
- # in different runtimes
- return None
- value = StringEncoding.EncodedString(self.value[start:stop:step])
- value.encoding = self.value.encoding
- if self.bytes_value is not None:
- bytes_value = StringEncoding.bytes_literal(
- self.bytes_value[start:stop:step], self.bytes_value.encoding)
- else:
- bytes_value = None
- return UnicodeNode(
- self.pos, value=value, bytes_value=bytes_value,
- constant_result=value)
- def coerce_to(self, dst_type, env):
- if dst_type is self.type:
- pass
- elif dst_type.is_unicode_char:
- if not self.can_coerce_to_char_literal():
- error(self.pos,
- "Only single-character Unicode string literals or "
- "surrogate pairs can be coerced into Py_UCS4/Py_UNICODE.")
- return self
- int_value = ord(self.value)
- return IntNode(self.pos, type=dst_type, value=str(int_value),
- constant_result=int_value)
- elif not dst_type.is_pyobject:
- if dst_type.is_string and self.bytes_value is not None:
- # special case: '-3' enforced unicode literal used in a
- # C char* context
- return BytesNode(self.pos, value=self.bytes_value
- ).coerce_to(dst_type, env)
- if dst_type.is_pyunicode_ptr:
- node = UnicodeNode(self.pos, value=self.value)
- node.type = dst_type
- return node
- error(self.pos,
- "Unicode literals do not support coercion to C types other "
- "than Py_UNICODE/Py_UCS4 (for characters) or Py_UNICODE* "
- "(for strings).")
- elif dst_type not in (py_object_type, Builtin.basestring_type):
- self.check_for_coercion_error(dst_type, env, fail=True)
- return self
- def can_coerce_to_char_literal(self):
- return len(self.value) == 1
- ## or (len(self.value) == 2
- ## and (0xD800 <= self.value[0] <= 0xDBFF)
- ## and (0xDC00 <= self.value[1] <= 0xDFFF))
- def coerce_to_boolean(self, env):
- bool_value = bool(self.value)
- return BoolNode(self.pos, value=bool_value, constant_result=bool_value)
- def contains_surrogates(self):
- return StringEncoding.string_contains_surrogates(self.value)
- def generate_evaluation_code(self, code):
- if self.type.is_pyobject:
- # FIXME: this should go away entirely!
- # Since string_contains_lone_surrogates() returns False for surrogate pairs in Py2/UCS2,
- # Py2 can generate different code from Py3 here. Let's hope we get away with claiming that
- # the processing of surrogate pairs in code was always ambiguous and lead to different results
- # on P16/32bit Unicode platforms.
- if StringEncoding.string_contains_lone_surrogates(self.value):
- # lone (unpaired) surrogates are not really portable and cannot be
- # decoded by the UTF-8 codec in Py3.3
- self.result_code = code.get_py_const(py_object_type, 'ustring')
- data_cname = code.get_string_const(
- StringEncoding.BytesLiteral(self.value.encode('unicode_escape')))
- const_code = code.get_cached_constants_writer(self.result_code)
- if const_code is None:
- return # already initialised
- const_code.mark_pos(self.pos)
- const_code.putln(
- "%s = PyUnicode_DecodeUnicodeEscape(%s, sizeof(%s) - 1, NULL); %s" % (
- self.result_code,
- data_cname,
- data_cname,
- const_code.error_goto_if_null(self.result_code, self.pos)))
- const_code.put_error_if_neg(
- self.pos, "__Pyx_PyUnicode_READY(%s)" % self.result_code)
- else:
- self.result_code = code.get_py_string_const(self.value)
- else:
- self.result_code = code.get_pyunicode_ptr_const(self.value)
- def calculate_result_code(self):
- return self.result_code
- def compile_time_value(self, env):
- return self.value
- class StringNode(PyConstNode):
- # A Python str object, i.e. a byte string in Python 2.x and a
- # unicode string in Python 3.x
- #
- # value BytesLiteral (or EncodedString with ASCII content)
- # unicode_value EncodedString or None
- # is_identifier boolean
- type = str_type
- is_string_literal = True
- is_identifier = None
- unicode_value = None
- def calculate_constant_result(self):
- if self.unicode_value is not None:
- # only the Unicode value is portable across Py2/3
- self.constant_result = self.unicode_value
- def analyse_as_type(self, env):
- return _analyse_name_as_type(self.unicode_value or self.value.decode('ISO8859-1'), self.pos, env)
- def as_sliced_node(self, start, stop, step=None):
- value = type(self.value)(self.value[start:stop:step])
- value.encoding = self.value.encoding
- if self.unicode_value is not None:
- if StringEncoding.string_contains_surrogates(self.unicode_value[:stop]):
- # this is unsafe as it may give different results in different runtimes
- return None
- unicode_value = StringEncoding.EncodedString(
- self.unicode_value[start:stop:step])
- else:
- unicode_value = None
- return StringNode(
- self.pos, value=value, unicode_value=unicode_value,
- constant_result=value, is_identifier=self.is_identifier)
- def coerce_to(self, dst_type, env):
- if dst_type is not py_object_type and not str_type.subtype_of(dst_type):
- # if dst_type is Builtin.bytes_type:
- # # special case: bytes = 'str literal'
- # return BytesNode(self.pos, value=self.value)
- if not dst_type.is_pyobject:
- return BytesNode(self.pos, value=self.value).coerce_to(dst_type, env)
- if dst_type is not Builtin.basestring_type:
- self.check_for_coercion_error(dst_type, env, fail=True)
- return self
- def can_coerce_to_char_literal(self):
- return not self.is_identifier and len(self.value) == 1
- def generate_evaluation_code(self, code):
- self.result_code = code.get_py_string_const(
- self.value, identifier=self.is_identifier, is_str=True,
- unicode_value=self.unicode_value)
- def get_constant_c_result_code(self):
- return None
- def calculate_result_code(self):
- return self.result_code
- def compile_time_value(self, env):
- if self.value.is_unicode:
- return self.value
- if not IS_PYTHON3:
- # use plain str/bytes object in Py2
- return self.value.byteencode()
- # in Py3, always return a Unicode string
- if self.unicode_value is not None:
- return self.unicode_value
- return self.value.decode('iso8859-1')
- class IdentifierStringNode(StringNode):
- # A special str value that represents an identifier (bytes in Py2,
- # unicode in Py3).
- is_identifier = True
- class ImagNode(AtomicExprNode):
- # Imaginary number literal
- #
- # value string imaginary part (float value)
- type = PyrexTypes.c_double_complex_type
- def calculate_constant_result(self):
- self.constant_result = complex(0.0, float(self.value))
- def compile_time_value(self, denv):
- return complex(0.0, float(self.value))
- def analyse_types(self, env):
- self.type.create_declaration_utility_code(env)
- return self
- def may_be_none(self):
- return False
- def coerce_to(self, dst_type, env):
- if self.type is dst_type:
- return self
- node = ImagNode(self.pos, value=self.value)
- if dst_type.is_pyobject:
- node.is_temp = 1
- node.type = Builtin.complex_type
- # We still need to perform normal coerce_to processing on the
- # result, because we might be coercing to an extension type,
- # in which case a type test node will be needed.
- return AtomicExprNode.coerce_to(node, dst_type, env)
- gil_message = "Constructing complex number"
- def calculate_result_code(self):
- if self.type.is_pyobject:
- return self.result()
- else:
- return "%s(0, %r)" % (self.type.from_parts, float(self.value))
- def generate_result_code(self, code):
- if self.type.is_pyobject:
- code.putln(
- "%s = PyComplex_FromDoubles(0.0, %r); %s" % (
- self.result(),
- float(self.value),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class NewExprNode(AtomicExprNode):
- # C++ new statement
- #
- # cppclass node c++ class to create
- type = None
- def infer_type(self, env):
- type = self.cppclass.analyse_as_type(env)
- if type is None or not type.is_cpp_class:
- error(self.pos, "new operator can only be applied to a C++ class")
- self.type = error_type
- return
- self.cpp_check(env)
- constructor = type.get_constructor(self.pos)
- self.class_type = type
- self.entry = constructor
- self.type = constructor.type
- return self.type
- def analyse_types(self, env):
- if self.type is None:
- self.infer_type(env)
- return self
- def may_be_none(self):
- return False
- def generate_result_code(self, code):
- pass
- def calculate_result_code(self):
- return "new " + self.class_type.empty_declaration_code()
- class NameNode(AtomicExprNode):
- # Reference to a local or global variable name.
- #
- # name string Python name of the variable
- # entry Entry Symbol table entry
- # type_entry Entry For extension type names, the original type entry
- # cf_is_null boolean Is uninitialized before this node
- # cf_maybe_null boolean Maybe uninitialized before this node
- # allow_null boolean Don't raise UnboundLocalError
- # nogil boolean Whether it is used in a nogil context
- is_name = True
- is_cython_module = False
- cython_attribute = None
- lhs_of_first_assignment = False # TODO: remove me
- is_used_as_rvalue = 0
- entry = None
- type_entry = None
- cf_maybe_null = True
- cf_is_null = False
- allow_null = False
- nogil = False
- inferred_type = None
- def as_cython_attribute(self):
- return self.cython_attribute
- def type_dependencies(self, env):
- if self.entry is None:
- self.entry = env.lookup(self.name)
- if self.entry is not None and self.entry.type.is_unspecified:
- return (self,)
- else:
- return ()
- def infer_type(self, env):
- if self.entry is None:
- self.entry = env.lookup(self.name)
- if self.entry is None or self.entry.type is unspecified_type:
- if self.inferred_type is not None:
- return self.inferred_type
- return py_object_type
- elif (self.entry.type.is_extension_type or self.entry.type.is_builtin_type) and \
- self.name == self.entry.type.name:
- # Unfortunately the type attribute of type objects
- # is used for the pointer to the type they represent.
- return type_type
- elif self.entry.type.is_cfunction:
- if self.entry.scope.is_builtin_scope:
- # special case: optimised builtin functions must be treated as Python objects
- return py_object_type
- else:
- # special case: referring to a C function must return its pointer
- return PyrexTypes.CPtrType(self.entry.type)
- else:
- # If entry is inferred as pyobject it's safe to use local
- # NameNode's inferred_type.
- if self.entry.type.is_pyobject and self.inferred_type:
- # Overflow may happen if integer
- if not (self.inferred_type.is_int and self.entry.might_overflow):
- return self.inferred_type
- return self.entry.type
- def compile_time_value(self, denv):
- try:
- return denv.lookup(self.name)
- except KeyError:
- error(self.pos, "Compile-time name '%s' not defined" % self.name)
- def get_constant_c_result_code(self):
- if not self.entry or self.entry.type.is_pyobject:
- return None
- return self.entry.cname
- def coerce_to(self, dst_type, env):
- # If coercing to a generic pyobject and this is a builtin
- # C function with a Python equivalent, manufacture a NameNode
- # referring to the Python builtin.
- #print "NameNode.coerce_to:", self.name, dst_type ###
- if dst_type is py_object_type:
- entry = self.entry
- if entry and entry.is_cfunction:
- var_entry = entry.as_variable
- if var_entry:
- if var_entry.is_builtin and var_entry.is_const:
- var_entry = env.declare_builtin(var_entry.name, self.pos)
- node = NameNode(self.pos, name = self.name)
- node.entry = var_entry
- node.analyse_rvalue_entry(env)
- return node
- return super(NameNode, self).coerce_to(dst_type, env)
- def declare_from_annotation(self, env, as_target=False):
- """Implements PEP 526 annotation typing in a fairly relaxed way.
- Annotations are ignored for global variables, Python class attributes and already declared variables.
- String literals are allowed and ignored.
- The ambiguous Python types 'int' and 'long' are ignored and the 'cython.int' form must be used instead.
- """
- if not env.directives['annotation_typing']:
- return
- if env.is_module_scope or env.is_py_class_scope:
- # annotations never create global cdef names and Python classes don't support them anyway
- return
- name = self.name
- if self.entry or env.lookup_here(name) is not None:
- # already declared => ignore annotation
- return
- annotation = self.annotation
- if annotation.is_string_literal:
- # name: "description" => not a type, but still a declared variable or attribute
- atype = None
- else:
- _, atype = analyse_type_annotation(annotation, env)
- if atype is None:
- atype = unspecified_type if as_target and env.directives['infer_types'] != False else py_object_type
- self.entry = env.declare_var(name, atype, self.pos, is_cdef=not as_target)
- self.entry.annotation = annotation
- def analyse_as_module(self, env):
- # Try to interpret this as a reference to a cimported module.
- # Returns the module scope, or None.
- entry = self.entry
- if not entry:
- entry = env.lookup(self.name)
- if entry and entry.as_module:
- return entry.as_module
- return None
- def analyse_as_type(self, env):
- if self.cython_attribute:
- type = PyrexTypes.parse_basic_type(self.cython_attribute)
- else:
- type = PyrexTypes.parse_basic_type(self.name)
- if type:
- return type
- entry = self.entry
- if not entry:
- entry = env.lookup(self.name)
- if entry and entry.is_type:
- return entry.type
- else:
- return None
- def analyse_as_extension_type(self, env):
- # Try to interpret this as a reference to an extension type.
- # Returns the extension type, or None.
- entry = self.entry
- if not entry:
- entry = env.lookup(self.name)
- if entry and entry.is_type:
- if entry.type.is_extension_type or entry.type.is_builtin_type:
- return entry.type
- return None
- def analyse_target_declaration(self, env):
- if not self.entry:
- self.entry = env.lookup_here(self.name)
- if not self.entry and self.annotation is not None:
- # name : type = ...
- self.declare_from_annotation(env, as_target=True)
- if not self.entry:
- if env.directives['warn.undeclared']:
- warning(self.pos, "implicit declaration of '%s'" % self.name, 1)
- if env.directives['infer_types'] != False:
- type = unspecified_type
- else:
- type = py_object_type
- self.entry = env.declare_var(self.name, type, self.pos)
- if self.entry.is_declared_generic:
- self.result_ctype = py_object_type
- if self.entry.as_module:
- # cimported modules namespace can shadow actual variables
- self.entry.is_variable = 1
- def analyse_types(self, env):
- self.initialized_check = env.directives['initializedcheck']
- entry = self.entry
- if entry is None:
- entry = env.lookup(self.name)
- if not entry:
- entry = env.declare_builtin(self.name, self.pos)
- if entry and entry.is_builtin and entry.is_const:
- self.is_literal = True
- if not entry:
- self.type = PyrexTypes.error_type
- return self
- self.entry = entry
- entry.used = 1
- if entry.type.is_buffer:
- from . import Buffer
- Buffer.used_buffer_aux_vars(entry)
- self.analyse_rvalue_entry(env)
- return self
- def analyse_target_types(self, env):
- self.analyse_entry(env, is_target=True)
- entry = self.entry
- if entry.is_cfunction and entry.as_variable:
- # FIXME: unify "is_overridable" flags below
- if (entry.is_overridable or entry.type.is_overridable) or not self.is_lvalue() and entry.fused_cfunction:
- # We need this for assigning to cpdef names and for the fused 'def' TreeFragment
- entry = self.entry = entry.as_variable
- self.type = entry.type
- if self.type.is_const:
- error(self.pos, "Assignment to const '%s'" % self.name)
- if self.type.is_reference:
- error(self.pos, "Assignment to reference '%s'" % self.name)
- if not self.is_lvalue():
- error(self.pos, "Assignment to non-lvalue '%s'" % self.name)
- self.type = PyrexTypes.error_type
- entry.used = 1
- if entry.type.is_buffer:
- from . import Buffer
- Buffer.used_buffer_aux_vars(entry)
- return self
- def analyse_rvalue_entry(self, env):
- #print "NameNode.analyse_rvalue_entry:", self.name ###
- #print "Entry:", self.entry.__dict__ ###
- self.analyse_entry(env)
- entry = self.entry
- if entry.is_declared_generic:
- self.result_ctype = py_object_type
- if entry.is_pyglobal or entry.is_builtin:
- if entry.is_builtin and entry.is_const:
- self.is_temp = 0
- else:
- self.is_temp = 1
- self.is_used_as_rvalue = 1
- elif entry.type.is_memoryviewslice:
- self.is_temp = False
- self.is_used_as_rvalue = True
- self.use_managed_ref = True
- return self
- def nogil_check(self, env):
- self.nogil = True
- if self.is_used_as_rvalue:
- entry = self.entry
- if entry.is_builtin:
- if not entry.is_const: # cached builtins are ok
- self.gil_error()
- elif entry.is_pyglobal:
- self.gil_error()
- gil_message = "Accessing Python global or builtin"
- def analyse_entry(self, env, is_target=False):
- #print "NameNode.analyse_entry:", self.name ###
- self.check_identifier_kind()
- entry = self.entry
- type = entry.type
- if (not is_target and type.is_pyobject and self.inferred_type and
- self.inferred_type.is_builtin_type):
- # assume that type inference is smarter than the static entry
- type = self.inferred_type
- self.type = type
- def check_identifier_kind(self):
- # Check that this is an appropriate kind of name for use in an
- # expression. Also finds the variable entry associated with
- # an extension type.
- entry = self.entry
- if entry.is_type and entry.type.is_extension_type:
- self.type_entry = entry
- if entry.is_type and entry.type.is_enum:
- py_entry = Symtab.Entry(self.name, None, py_object_type)
- py_entry.is_pyglobal = True
- py_entry.scope = self.entry.scope
- self.entry = py_entry
- elif not (entry.is_const or entry.is_variable or
- entry.is_builtin or entry.is_cfunction or
- entry.is_cpp_class):
- if self.entry.as_variable:
- self.entry = self.entry.as_variable
- elif not self.is_cython_module:
- error(self.pos, "'%s' is not a constant, variable or function identifier" % self.name)
- def is_cimported_module_without_shadow(self, env):
- if self.is_cython_module or self.cython_attribute:
- return False
- entry = self.entry or env.lookup(self.name)
- return entry.as_module and not entry.is_variable
- def is_simple(self):
- # If it's not a C variable, it'll be in a temp.
- return 1
- def may_be_none(self):
- if self.cf_state and self.type and (self.type.is_pyobject or
- self.type.is_memoryviewslice):
- # gard against infinite recursion on self-dependencies
- if getattr(self, '_none_checking', False):
- # self-dependency - either this node receives a None
- # value from *another* node, or it can not reference
- # None at this point => safe to assume "not None"
- return False
- self._none_checking = True
- # evaluate control flow state to see if there were any
- # potential None values assigned to the node so far
- may_be_none = False
- for assignment in self.cf_state:
- if assignment.rhs.may_be_none():
- may_be_none = True
- break
- del self._none_checking
- return may_be_none
- return super(NameNode, self).may_be_none()
- def nonlocally_immutable(self):
- if ExprNode.nonlocally_immutable(self):
- return True
- entry = self.entry
- if not entry or entry.in_closure:
- return False
- return entry.is_local or entry.is_arg or entry.is_builtin or entry.is_readonly
- def calculate_target_results(self, env):
- pass
- def check_const(self):
- entry = self.entry
- if entry is not None and not (
- entry.is_const or
- entry.is_cfunction or
- entry.is_builtin or
- entry.type.is_const):
- self.not_const()
- return False
- return True
- def check_const_addr(self):
- entry = self.entry
- if not (entry.is_cglobal or entry.is_cfunction or entry.is_builtin):
- self.addr_not_const()
- return False
- return True
- def is_lvalue(self):
- return (
- self.entry.is_variable and
- not self.entry.is_readonly
- ) or (
- self.entry.is_cfunction and
- self.entry.is_overridable
- )
- def is_addressable(self):
- return self.entry.is_variable and not self.type.is_memoryviewslice
- def is_ephemeral(self):
- # Name nodes are never ephemeral, even if the
- # result is in a temporary.
- return 0
- def calculate_result_code(self):
- entry = self.entry
- if not entry:
- return "<error>" # There was an error earlier
- return entry.cname
- def generate_result_code(self, code):
- assert hasattr(self, 'entry')
- entry = self.entry
- if entry is None:
- return # There was an error earlier
- if entry.utility_code:
- code.globalstate.use_utility_code(entry.utility_code)
- if entry.is_builtin and entry.is_const:
- return # Lookup already cached
- elif entry.is_pyclass_attr:
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- if entry.is_builtin:
- namespace = Naming.builtins_cname
- else: # entry.is_pyglobal
- namespace = entry.scope.namespace_cname
- if not self.cf_is_null:
- code.putln(
- '%s = PyObject_GetItem(%s, %s);' % (
- self.result(),
- namespace,
- interned_cname))
- code.putln('if (unlikely(!%s)) {' % self.result())
- code.putln('PyErr_Clear();')
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
- code.putln(
- '__Pyx_GetModuleGlobalName(%s, %s);' % (
- self.result(),
- interned_cname))
- if not self.cf_is_null:
- code.putln("}")
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- elif entry.is_builtin and not entry.scope.is_module_scope:
- # known builtin
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c"))
- code.putln(
- '%s = __Pyx_GetBuiltinName(%s); %s' % (
- self.result(),
- interned_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif entry.is_pyglobal or (entry.is_builtin and entry.scope.is_module_scope):
- # name in class body, global name or unknown builtin
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- if entry.scope.is_module_scope:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetModuleGlobalName", "ObjectHandling.c"))
- code.putln(
- '__Pyx_GetModuleGlobalName(%s, %s); %s' % (
- self.result(),
- interned_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- else:
- # FIXME: is_pyglobal is also used for class namespace
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("GetNameInClass", "ObjectHandling.c"))
- code.putln(
- '__Pyx_GetNameInClass(%s, %s, %s); %s' % (
- self.result(),
- entry.scope.namespace_cname,
- interned_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif entry.is_local or entry.in_closure or entry.from_closure or entry.type.is_memoryviewslice:
- # Raise UnboundLocalError for objects and memoryviewslices
- raise_unbound = (
- (self.cf_maybe_null or self.cf_is_null) and not self.allow_null)
- null_code = entry.type.check_for_null_code(entry.cname)
- memslice_check = entry.type.is_memoryviewslice and self.initialized_check
- if null_code and raise_unbound and (entry.type.is_pyobject or memslice_check):
- code.put_error_if_unbound(self.pos, entry, self.in_nogil_context)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- #print "NameNode.generate_assignment_code:", self.name ###
- entry = self.entry
- if entry is None:
- return # There was an error earlier
- if (self.entry.type.is_ptr and isinstance(rhs, ListNode)
- and not self.lhs_of_first_assignment and not rhs.in_module_scope):
- error(self.pos, "Literal list must be assigned to pointer at time of declaration")
- # is_pyglobal seems to be True for module level-globals only.
- # We use this to access class->tp_dict if necessary.
- if entry.is_pyglobal:
- assert entry.type.is_pyobject, "Python global or builtin not a Python object"
- interned_cname = code.intern_identifier(self.entry.name)
- namespace = self.entry.scope.namespace_cname
- if entry.is_member:
- # if the entry is a member we have to cheat: SetAttr does not work
- # on types, so we create a descriptor which is then added to tp_dict
- setter = 'PyDict_SetItem'
- namespace = '%s->tp_dict' % namespace
- elif entry.scope.is_module_scope:
- setter = 'PyDict_SetItem'
- namespace = Naming.moddict_cname
- elif entry.is_pyclass_attr:
- code.globalstate.use_utility_code(UtilityCode.load_cached("SetNameInClass", "ObjectHandling.c"))
- setter = '__Pyx_SetNameInClass'
- else:
- assert False, repr(entry)
- code.put_error_if_neg(
- self.pos,
- '%s(%s, %s, %s)' % (
- setter,
- namespace,
- interned_cname,
- rhs.py_result()))
- if debug_disposal_code:
- print("NameNode.generate_assignment_code:")
- print("...generating disposal code for %s" % rhs)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- if entry.is_member:
- # in Py2.6+, we need to invalidate the method cache
- code.putln("PyType_Modified(%s);" %
- entry.scope.parent_type.typeptr_cname)
- else:
- if self.type.is_memoryviewslice:
- self.generate_acquire_memoryviewslice(rhs, code)
- elif self.type.is_buffer:
- # Generate code for doing the buffer release/acquisition.
- # This might raise an exception in which case the assignment (done
- # below) will not happen.
- #
- # The reason this is not in a typetest-like node is because the
- # variables that the acquired buffer info is stored to is allocated
- # per entry and coupled with it.
- self.generate_acquire_buffer(rhs, code)
- assigned = False
- if self.type.is_pyobject:
- #print "NameNode.generate_assignment_code: to", self.name ###
- #print "...from", rhs ###
- #print "...LHS type", self.type, "ctype", self.ctype() ###
- #print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
- if self.use_managed_ref:
- rhs.make_owned_reference(code)
- is_external_ref = entry.is_cglobal or self.entry.in_closure or self.entry.from_closure
- if is_external_ref:
- if not self.cf_is_null:
- if self.cf_maybe_null:
- code.put_xgotref(self.py_result())
- else:
- code.put_gotref(self.py_result())
- assigned = True
- if entry.is_cglobal:
- code.put_decref_set(
- self.result(), rhs.result_as(self.ctype()))
- else:
- if not self.cf_is_null:
- if self.cf_maybe_null:
- code.put_xdecref_set(
- self.result(), rhs.result_as(self.ctype()))
- else:
- code.put_decref_set(
- self.result(), rhs.result_as(self.ctype()))
- else:
- assigned = False
- if is_external_ref:
- code.put_giveref(rhs.py_result())
- if not self.type.is_memoryviewslice:
- if not assigned:
- if overloaded_assignment:
- result = rhs.result()
- if exception_check == '+':
- translate_cpp_exception(
- code, self.pos,
- '%s = %s;' % (self.result(), result),
- self.result() if self.type.is_pyobject else None,
- exception_value, self.in_nogil_context)
- else:
- code.putln('%s = %s;' % (self.result(), result))
- else:
- result = rhs.result_as(self.ctype())
- if is_pythran_expr(self.type):
- code.putln('new (&%s) decltype(%s){%s};' % (self.result(), self.result(), result))
- elif result != self.result():
- code.putln('%s = %s;' % (self.result(), result))
- if debug_disposal_code:
- print("NameNode.generate_assignment_code:")
- print("...generating post-assignment code for %s" % rhs)
- rhs.generate_post_assignment_code(code)
- elif rhs.result_in_temp():
- rhs.generate_post_assignment_code(code)
- rhs.free_temps(code)
- def generate_acquire_memoryviewslice(self, rhs, code):
- """
- Slices, coercions from objects, return values etc are new references.
- We have a borrowed reference in case of dst = src
- """
- from . import MemoryView
- MemoryView.put_acquire_memoryviewslice(
- lhs_cname=self.result(),
- lhs_type=self.type,
- lhs_pos=self.pos,
- rhs=rhs,
- code=code,
- have_gil=not self.in_nogil_context,
- first_assignment=self.cf_is_null)
- def generate_acquire_buffer(self, rhs, code):
- # rhstmp is only used in case the rhs is a complicated expression leading to
- # the object, to avoid repeating the same C expression for every reference
- # to the rhs. It does NOT hold a reference.
- pretty_rhs = isinstance(rhs, NameNode) or rhs.is_temp
- if pretty_rhs:
- rhstmp = rhs.result_as(self.ctype())
- else:
- rhstmp = code.funcstate.allocate_temp(self.entry.type, manage_ref=False)
- code.putln('%s = %s;' % (rhstmp, rhs.result_as(self.ctype())))
- from . import Buffer
- Buffer.put_assign_to_buffer(self.result(), rhstmp, self.entry,
- is_initialized=not self.lhs_of_first_assignment,
- pos=self.pos, code=code)
- if not pretty_rhs:
- code.putln("%s = 0;" % rhstmp)
- code.funcstate.release_temp(rhstmp)
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- if self.entry is None:
- return # There was an error earlier
- elif self.entry.is_pyclass_attr:
- namespace = self.entry.scope.namespace_cname
- interned_cname = code.intern_identifier(self.entry.name)
- if ignore_nonexisting:
- key_error_code = 'PyErr_Clear(); else'
- else:
- # minor hack: fake a NameError on KeyError
- key_error_code = (
- '{ PyErr_Clear(); PyErr_Format(PyExc_NameError, "name \'%%s\' is not defined", "%s"); }' %
- self.entry.name)
- code.putln(
- 'if (unlikely(PyObject_DelItem(%s, %s) < 0)) {'
- ' if (likely(PyErr_ExceptionMatches(PyExc_KeyError))) %s'
- ' %s '
- '}' % (namespace, interned_cname,
- key_error_code,
- code.error_goto(self.pos)))
- elif self.entry.is_pyglobal:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
- interned_cname = code.intern_identifier(self.entry.name)
- del_code = '__Pyx_PyObject_DelAttrStr(%s, %s)' % (
- Naming.module_cname, interned_cname)
- if ignore_nonexisting:
- code.putln(
- 'if (unlikely(%s < 0)) {'
- ' if (likely(PyErr_ExceptionMatches(PyExc_AttributeError))) PyErr_Clear(); else %s '
- '}' % (del_code, code.error_goto(self.pos)))
- else:
- code.put_error_if_neg(self.pos, del_code)
- elif self.entry.type.is_pyobject or self.entry.type.is_memoryviewslice:
- if not self.cf_is_null:
- if self.cf_maybe_null and not ignore_nonexisting:
- code.put_error_if_unbound(self.pos, self.entry)
- if self.entry.type.is_pyobject:
- if self.entry.in_closure:
- # generator
- if ignore_nonexisting and self.cf_maybe_null:
- code.put_xgotref(self.result())
- else:
- code.put_gotref(self.result())
- if ignore_nonexisting and self.cf_maybe_null:
- code.put_xdecref(self.result(), self.ctype())
- else:
- code.put_decref(self.result(), self.ctype())
- code.putln('%s = NULL;' % self.result())
- else:
- code.put_xdecref_memoryviewslice(self.entry.cname,
- have_gil=not self.nogil)
- else:
- error(self.pos, "Deletion of C names not supported")
- def annotate(self, code):
- if hasattr(self, 'is_called') and self.is_called:
- pos = (self.pos[0], self.pos[1], self.pos[2] - len(self.name) - 1)
- if self.type.is_pyobject:
- style, text = 'py_call', 'python function (%s)'
- else:
- style, text = 'c_call', 'c function (%s)'
- code.annotate(pos, AnnotationItem(style, text % self.type, size=len(self.name)))
- class BackquoteNode(ExprNode):
- # `expr`
- #
- # arg ExprNode
- type = py_object_type
- subexprs = ['arg']
- def analyse_types(self, env):
- self.arg = self.arg.analyse_types(env)
- self.arg = self.arg.coerce_to_pyobject(env)
- self.is_temp = 1
- return self
- gil_message = "Backquote expression"
- def calculate_constant_result(self):
- self.constant_result = repr(self.arg.constant_result)
- def generate_result_code(self, code):
- code.putln(
- "%s = PyObject_Repr(%s); %s" % (
- self.result(),
- self.arg.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class ImportNode(ExprNode):
- # Used as part of import statement implementation.
- # Implements result =
- # __import__(module_name, globals(), None, name_list, level)
- #
- # module_name StringNode dotted name of module. Empty module
- # name means importing the parent package according
- # to level
- # name_list ListNode or None list of names to be imported
- # level int relative import level:
- # -1: attempt both relative import and absolute import;
- # 0: absolute import;
- # >0: the number of parent directories to search
- # relative to the current module.
- # None: decide the level according to language level and
- # directives
- type = py_object_type
- subexprs = ['module_name', 'name_list']
- def analyse_types(self, env):
- if self.level is None:
- if (env.directives['py2_import'] or
- Future.absolute_import not in env.global_scope().context.future_directives):
- self.level = -1
- else:
- self.level = 0
- module_name = self.module_name.analyse_types(env)
- self.module_name = module_name.coerce_to_pyobject(env)
- if self.name_list:
- name_list = self.name_list.analyse_types(env)
- self.name_list = name_list.coerce_to_pyobject(env)
- self.is_temp = 1
- return self
- gil_message = "Python import"
- def generate_result_code(self, code):
- if self.name_list:
- name_list_code = self.name_list.py_result()
- else:
- name_list_code = "0"
- code.globalstate.use_utility_code(UtilityCode.load_cached("Import", "ImportExport.c"))
- import_code = "__Pyx_Import(%s, %s, %d)" % (
- self.module_name.py_result(),
- name_list_code,
- self.level)
- if (self.level <= 0 and
- self.module_name.is_string_literal and
- self.module_name.value in utility_code_for_imports):
- helper_func, code_name, code_file = utility_code_for_imports[self.module_name.value]
- code.globalstate.use_utility_code(UtilityCode.load_cached(code_name, code_file))
- import_code = '%s(%s)' % (helper_func, import_code)
- code.putln("%s = %s; %s" % (
- self.result(),
- import_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class IteratorNode(ExprNode):
- # Used as part of for statement implementation.
- #
- # Implements result = iter(sequence)
- #
- # sequence ExprNode
- type = py_object_type
- iter_func_ptr = None
- counter_cname = None
- cpp_iterator_cname = None
- reversed = False # currently only used for list/tuple types (see Optimize.py)
- is_async = False
- subexprs = ['sequence']
- def analyse_types(self, env):
- self.sequence = self.sequence.analyse_types(env)
- if (self.sequence.type.is_array or self.sequence.type.is_ptr) and \
- not self.sequence.type.is_string:
- # C array iteration will be transformed later on
- self.type = self.sequence.type
- elif self.sequence.type.is_cpp_class:
- self.analyse_cpp_types(env)
- else:
- self.sequence = self.sequence.coerce_to_pyobject(env)
- if self.sequence.type in (list_type, tuple_type):
- self.sequence = self.sequence.as_none_safe_node("'NoneType' object is not iterable")
- self.is_temp = 1
- return self
- gil_message = "Iterating over Python object"
- _func_iternext_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(
- PyrexTypes.py_object_type, [
- PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None),
- ]))
- def type_dependencies(self, env):
- return self.sequence.type_dependencies(env)
- def infer_type(self, env):
- sequence_type = self.sequence.infer_type(env)
- if sequence_type.is_array or sequence_type.is_ptr:
- return sequence_type
- elif sequence_type.is_cpp_class:
- begin = sequence_type.scope.lookup("begin")
- if begin is not None:
- return begin.type.return_type
- elif sequence_type.is_pyobject:
- return sequence_type
- return py_object_type
- def analyse_cpp_types(self, env):
- sequence_type = self.sequence.type
- if sequence_type.is_ptr:
- sequence_type = sequence_type.base_type
- begin = sequence_type.scope.lookup("begin")
- end = sequence_type.scope.lookup("end")
- if (begin is None
- or not begin.type.is_cfunction
- or begin.type.args):
- error(self.pos, "missing begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if (end is None
- or not end.type.is_cfunction
- or end.type.args):
- error(self.pos, "missing end() on %s" % self.sequence.type)
- self.type = error_type
- return
- iter_type = begin.type.return_type
- if iter_type.is_cpp_class:
- if env.lookup_operator_for_types(
- self.pos,
- "!=",
- [iter_type, end.type.return_type]) is None:
- error(self.pos, "missing operator!= on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if env.lookup_operator_for_types(self.pos, '++', [iter_type]) is None:
- error(self.pos, "missing operator++ on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- if env.lookup_operator_for_types(self.pos, '*', [iter_type]) is None:
- error(self.pos, "missing operator* on result of begin() on %s" % self.sequence.type)
- self.type = error_type
- return
- self.type = iter_type
- elif iter_type.is_ptr:
- if not (iter_type == end.type.return_type):
- error(self.pos, "incompatible types for begin() and end()")
- self.type = iter_type
- else:
- error(self.pos, "result type of begin() on %s must be a C++ class or pointer" % self.sequence.type)
- self.type = error_type
- return
- def generate_result_code(self, code):
- sequence_type = self.sequence.type
- if sequence_type.is_cpp_class:
- if self.sequence.is_name:
- # safe: C++ won't allow you to reassign to class references
- begin_func = "%s.begin" % self.sequence.result()
- else:
- sequence_type = PyrexTypes.c_ptr_type(sequence_type)
- self.cpp_iterator_cname = code.funcstate.allocate_temp(sequence_type, manage_ref=False)
- code.putln("%s = &%s;" % (self.cpp_iterator_cname, self.sequence.result()))
- begin_func = "%s->begin" % self.cpp_iterator_cname
- # TODO: Limit scope.
- code.putln("%s = %s();" % (self.result(), begin_func))
- return
- if sequence_type.is_array or sequence_type.is_ptr:
- raise InternalError("for in carray slice not transformed")
- is_builtin_sequence = sequence_type in (list_type, tuple_type)
- if not is_builtin_sequence:
- # reversed() not currently optimised (see Optimize.py)
- assert not self.reversed, "internal error: reversed() only implemented for list/tuple objects"
- self.may_be_a_sequence = not sequence_type.is_builtin_type
- if self.may_be_a_sequence:
- code.putln(
- "if (likely(PyList_CheckExact(%s)) || PyTuple_CheckExact(%s)) {" % (
- self.sequence.py_result(),
- self.sequence.py_result()))
- if is_builtin_sequence or self.may_be_a_sequence:
- self.counter_cname = code.funcstate.allocate_temp(
- PyrexTypes.c_py_ssize_t_type, manage_ref=False)
- if self.reversed:
- if sequence_type is list_type:
- init_value = 'PyList_GET_SIZE(%s) - 1' % self.result()
- else:
- init_value = 'PyTuple_GET_SIZE(%s) - 1' % self.result()
- else:
- init_value = '0'
- code.putln("%s = %s; __Pyx_INCREF(%s); %s = %s;" % (
- self.result(),
- self.sequence.py_result(),
- self.result(),
- self.counter_cname,
- init_value))
- if not is_builtin_sequence:
- self.iter_func_ptr = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False)
- if self.may_be_a_sequence:
- code.putln("%s = NULL;" % self.iter_func_ptr)
- code.putln("} else {")
- code.put("%s = -1; " % self.counter_cname)
- code.putln("%s = PyObject_GetIter(%s); %s" % (
- self.result(),
- self.sequence.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- # PyObject_GetIter() fails if "tp_iternext" is not set, but the check below
- # makes it visible to the C compiler that the pointer really isn't NULL, so that
- # it can distinguish between the special cases and the generic case
- code.putln("%s = Py_TYPE(%s)->tp_iternext; %s" % (
- self.iter_func_ptr, self.py_result(),
- code.error_goto_if_null(self.iter_func_ptr, self.pos)))
- if self.may_be_a_sequence:
- code.putln("}")
- def generate_next_sequence_item(self, test_name, result_name, code):
- assert self.counter_cname, "internal error: counter_cname temp not prepared"
- final_size = 'Py%s_GET_SIZE(%s)' % (test_name, self.py_result())
- if self.sequence.is_sequence_constructor:
- item_count = len(self.sequence.args)
- if self.sequence.mult_factor is None:
- final_size = item_count
- elif isinstance(self.sequence.mult_factor.constant_result, _py_int_types):
- final_size = item_count * self.sequence.mult_factor.constant_result
- code.putln("if (%s >= %s) break;" % (self.counter_cname, final_size))
- if self.reversed:
- inc_dec = '--'
- else:
- inc_dec = '++'
- code.putln("#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS")
- code.putln(
- "%s = Py%s_GET_ITEM(%s, %s); __Pyx_INCREF(%s); %s%s; %s" % (
- result_name,
- test_name,
- self.py_result(),
- self.counter_cname,
- result_name,
- self.counter_cname,
- inc_dec,
- # use the error label to avoid C compiler warnings if we only use it below
- code.error_goto_if_neg('0', self.pos)
- ))
- code.putln("#else")
- code.putln(
- "%s = PySequence_ITEM(%s, %s); %s%s; %s" % (
- result_name,
- self.py_result(),
- self.counter_cname,
- self.counter_cname,
- inc_dec,
- code.error_goto_if_null(result_name, self.pos)))
- code.put_gotref(result_name)
- code.putln("#endif")
- def generate_iter_next_result_code(self, result_name, code):
- sequence_type = self.sequence.type
- if self.reversed:
- code.putln("if (%s < 0) break;" % self.counter_cname)
- if sequence_type.is_cpp_class:
- if self.cpp_iterator_cname:
- end_func = "%s->end" % self.cpp_iterator_cname
- else:
- end_func = "%s.end" % self.sequence.result()
- # TODO: Cache end() call?
- code.putln("if (!(%s != %s())) break;" % (
- self.result(),
- end_func))
- code.putln("%s = *%s;" % (
- result_name,
- self.result()))
- code.putln("++%s;" % self.result())
- return
- elif sequence_type is list_type:
- self.generate_next_sequence_item('List', result_name, code)
- return
- elif sequence_type is tuple_type:
- self.generate_next_sequence_item('Tuple', result_name, code)
- return
- if self.may_be_a_sequence:
- code.putln("if (likely(!%s)) {" % self.iter_func_ptr)
- code.putln("if (likely(PyList_CheckExact(%s))) {" % self.py_result())
- self.generate_next_sequence_item('List', result_name, code)
- code.putln("} else {")
- self.generate_next_sequence_item('Tuple', result_name, code)
- code.putln("}")
- code.put("} else ")
- code.putln("{")
- code.putln(
- "%s = %s(%s);" % (
- result_name,
- self.iter_func_ptr,
- self.py_result()))
- code.putln("if (unlikely(!%s)) {" % result_name)
- code.putln("PyObject* exc_type = PyErr_Occurred();")
- code.putln("if (exc_type) {")
- code.putln("if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();")
- code.putln("else %s" % code.error_goto(self.pos))
- code.putln("}")
- code.putln("break;")
- code.putln("}")
- code.put_gotref(result_name)
- code.putln("}")
- def free_temps(self, code):
- if self.counter_cname:
- code.funcstate.release_temp(self.counter_cname)
- if self.iter_func_ptr:
- code.funcstate.release_temp(self.iter_func_ptr)
- self.iter_func_ptr = None
- if self.cpp_iterator_cname:
- code.funcstate.release_temp(self.cpp_iterator_cname)
- ExprNode.free_temps(self, code)
- class NextNode(AtomicExprNode):
- # Used as part of for statement implementation.
- # Implements result = next(iterator)
- # Created during analyse_types phase.
- # The iterator is not owned by this node.
- #
- # iterator IteratorNode
- def __init__(self, iterator):
- AtomicExprNode.__init__(self, iterator.pos)
- self.iterator = iterator
- def nogil_check(self, env):
- # ignore - errors (if any) are already handled by IteratorNode
- pass
- def type_dependencies(self, env):
- return self.iterator.type_dependencies(env)
- def infer_type(self, env, iterator_type=None):
- if iterator_type is None:
- iterator_type = self.iterator.infer_type(env)
- if iterator_type.is_ptr or iterator_type.is_array:
- return iterator_type.base_type
- elif iterator_type.is_cpp_class:
- item_type = env.lookup_operator_for_types(self.pos, "*", [iterator_type]).type.return_type
- if item_type.is_reference:
- item_type = item_type.ref_base_type
- if item_type.is_const:
- item_type = item_type.const_base_type
- return item_type
- else:
- # Avoid duplication of complicated logic.
- fake_index_node = IndexNode(
- self.pos,
- base=self.iterator.sequence,
- index=IntNode(self.pos, value='PY_SSIZE_T_MAX',
- type=PyrexTypes.c_py_ssize_t_type))
- return fake_index_node.infer_type(env)
- def analyse_types(self, env):
- self.type = self.infer_type(env, self.iterator.type)
- self.is_temp = 1
- return self
- def generate_result_code(self, code):
- self.iterator.generate_iter_next_result_code(self.result(), code)
- class AsyncIteratorNode(ExprNode):
- # Used as part of 'async for' statement implementation.
- #
- # Implements result = sequence.__aiter__()
- #
- # sequence ExprNode
- subexprs = ['sequence']
- is_async = True
- type = py_object_type
- is_temp = 1
- def infer_type(self, env):
- return py_object_type
- def analyse_types(self, env):
- self.sequence = self.sequence.analyse_types(env)
- if not self.sequence.type.is_pyobject:
- error(self.pos, "async for loops not allowed on C/C++ types")
- self.sequence = self.sequence.coerce_to_pyobject(env)
- return self
- def generate_result_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("AsyncIter", "Coroutine.c"))
- code.putln("%s = __Pyx_Coroutine_GetAsyncIter(%s); %s" % (
- self.result(),
- self.sequence.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- class AsyncNextNode(AtomicExprNode):
- # Used as part of 'async for' statement implementation.
- # Implements result = iterator.__anext__()
- # Created during analyse_types phase.
- # The iterator is not owned by this node.
- #
- # iterator IteratorNode
- type = py_object_type
- is_temp = 1
- def __init__(self, iterator):
- AtomicExprNode.__init__(self, iterator.pos)
- self.iterator = iterator
- def infer_type(self, env):
- return py_object_type
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("AsyncIter", "Coroutine.c"))
- code.putln("%s = __Pyx_Coroutine_AsyncIterNext(%s); %s" % (
- self.result(),
- self.iterator.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- class WithExitCallNode(ExprNode):
- # The __exit__() call of a 'with' statement. Used in both the
- # except and finally clauses.
- # with_stat WithStatNode the surrounding 'with' statement
- # args TupleNode or ResultStatNode the exception info tuple
- # await_expr AwaitExprNode the await expression of an 'async with' statement
- subexprs = ['args', 'await_expr']
- test_if_run = True
- await_expr = None
- def analyse_types(self, env):
- self.args = self.args.analyse_types(env)
- if self.await_expr:
- self.await_expr = self.await_expr.analyse_types(env)
- self.type = PyrexTypes.c_bint_type
- self.is_temp = True
- return self
- def generate_evaluation_code(self, code):
- if self.test_if_run:
- # call only if it was not already called (and decref-cleared)
- code.putln("if (%s) {" % self.with_stat.exit_var)
- self.args.generate_evaluation_code(code)
- result_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False)
- code.mark_pos(self.pos)
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCall", "ObjectHandling.c"))
- code.putln("%s = __Pyx_PyObject_Call(%s, %s, NULL);" % (
- result_var,
- self.with_stat.exit_var,
- self.args.result()))
- code.put_decref_clear(self.with_stat.exit_var, type=py_object_type)
- self.args.generate_disposal_code(code)
- self.args.free_temps(code)
- code.putln(code.error_goto_if_null(result_var, self.pos))
- code.put_gotref(result_var)
- if self.await_expr:
- # FIXME: result_var temp currently leaks into the closure
- self.await_expr.generate_evaluation_code(code, source_cname=result_var, decref_source=True)
- code.putln("%s = %s;" % (result_var, self.await_expr.py_result()))
- self.await_expr.generate_post_assignment_code(code)
- self.await_expr.free_temps(code)
- if self.result_is_used:
- self.allocate_temp_result(code)
- code.putln("%s = __Pyx_PyObject_IsTrue(%s);" % (self.result(), result_var))
- code.put_decref_clear(result_var, type=py_object_type)
- if self.result_is_used:
- code.put_error_if_neg(self.pos, self.result())
- code.funcstate.release_temp(result_var)
- if self.test_if_run:
- code.putln("}")
- class ExcValueNode(AtomicExprNode):
- # Node created during analyse_types phase
- # of an ExceptClauseNode to fetch the current
- # exception value.
- type = py_object_type
- def __init__(self, pos):
- ExprNode.__init__(self, pos)
- def set_var(self, var):
- self.var = var
- def calculate_result_code(self):
- return self.var
- def generate_result_code(self, code):
- pass
- def analyse_types(self, env):
- return self
- class TempNode(ExprNode):
- # Node created during analyse_types phase
- # of some nodes to hold a temporary value.
- #
- # Note: One must call "allocate" and "release" on
- # the node during code generation to get/release the temp.
- # This is because the temp result is often used outside of
- # the regular cycle.
- subexprs = []
- def __init__(self, pos, type, env=None):
- ExprNode.__init__(self, pos)
- self.type = type
- if type.is_pyobject:
- self.result_ctype = py_object_type
- self.is_temp = 1
- def analyse_types(self, env):
- return self
- def analyse_target_declaration(self, env):
- pass
- def generate_result_code(self, code):
- pass
- def allocate(self, code):
- self.temp_cname = code.funcstate.allocate_temp(self.type, manage_ref=True)
- def release(self, code):
- code.funcstate.release_temp(self.temp_cname)
- self.temp_cname = None
- def result(self):
- try:
- return self.temp_cname
- except:
- assert False, "Remember to call allocate/release on TempNode"
- raise
- # Do not participate in normal temp alloc/dealloc:
- def allocate_temp_result(self, code):
- pass
- def release_temp_result(self, code):
- pass
- class PyTempNode(TempNode):
- # TempNode holding a Python value.
- def __init__(self, pos, env):
- TempNode.__init__(self, pos, PyrexTypes.py_object_type, env)
- class RawCNameExprNode(ExprNode):
- subexprs = []
- def __init__(self, pos, type=None, cname=None):
- ExprNode.__init__(self, pos, type=type)
- if cname is not None:
- self.cname = cname
- def analyse_types(self, env):
- return self
- def set_cname(self, cname):
- self.cname = cname
- def result(self):
- return self.cname
- def generate_result_code(self, code):
- pass
- #-------------------------------------------------------------------
- #
- # F-strings
- #
- #-------------------------------------------------------------------
- class JoinedStrNode(ExprNode):
- # F-strings
- #
- # values [UnicodeNode|FormattedValueNode] Substrings of the f-string
- #
- type = unicode_type
- is_temp = True
- subexprs = ['values']
- def analyse_types(self, env):
- self.values = [v.analyse_types(env).coerce_to_pyobject(env) for v in self.values]
- return self
- def may_be_none(self):
- # PyUnicode_Join() always returns a Unicode string or raises an exception
- return False
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- num_items = len(self.values)
- list_var = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- ulength_var = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False)
- max_char_var = code.funcstate.allocate_temp(PyrexTypes.c_py_ucs4_type, manage_ref=False)
- code.putln('%s = PyTuple_New(%s); %s' % (
- list_var,
- num_items,
- code.error_goto_if_null(list_var, self.pos)))
- code.put_gotref(list_var)
- code.putln("%s = 0;" % ulength_var)
- code.putln("%s = 127;" % max_char_var) # at least ASCII character range
- for i, node in enumerate(self.values):
- node.generate_evaluation_code(code)
- node.make_owned_reference(code)
- ulength = "__Pyx_PyUnicode_GET_LENGTH(%s)" % node.py_result()
- max_char_value = "__Pyx_PyUnicode_MAX_CHAR_VALUE(%s)" % node.py_result()
- is_ascii = False
- if isinstance(node, UnicodeNode):
- try:
- # most strings will be ASCII or at least Latin-1
- node.value.encode('iso8859-1')
- max_char_value = '255'
- node.value.encode('us-ascii')
- is_ascii = True
- except UnicodeEncodeError:
- if max_char_value != '255':
- # not ISO8859-1 => check BMP limit
- max_char = max(map(ord, node.value))
- if max_char < 0xD800:
- # BMP-only, no surrogate pairs used
- max_char_value = '65535'
- ulength = str(len(node.value))
- elif max_char >= 65536:
- # cleary outside of BMP, and not on a 16-bit Unicode system
- max_char_value = '1114111'
- ulength = str(len(node.value))
- else:
- # not really worth implementing a check for surrogate pairs here
- # drawback: C code can differ when generating on Py2 with 2-byte Unicode
- pass
- else:
- ulength = str(len(node.value))
- elif isinstance(node, FormattedValueNode) and node.value.type.is_numeric:
- is_ascii = True # formatted C numbers are always ASCII
- if not is_ascii:
- code.putln("%s = (%s > %s) ? %s : %s;" % (
- max_char_var, max_char_value, max_char_var, max_char_value, max_char_var))
- code.putln("%s += %s;" % (ulength_var, ulength))
- code.put_giveref(node.py_result())
- code.putln('PyTuple_SET_ITEM(%s, %s, %s);' % (list_var, i, node.py_result()))
- node.generate_post_assignment_code(code)
- node.free_temps(code)
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- code.globalstate.use_utility_code(UtilityCode.load_cached("JoinPyUnicode", "StringTools.c"))
- code.putln('%s = __Pyx_PyUnicode_Join(%s, %d, %s, %s); %s' % (
- self.result(),
- list_var,
- num_items,
- ulength_var,
- max_char_var,
- code.error_goto_if_null(self.py_result(), self.pos)))
- code.put_gotref(self.py_result())
- code.put_decref_clear(list_var, py_object_type)
- code.funcstate.release_temp(list_var)
- code.funcstate.release_temp(ulength_var)
- code.funcstate.release_temp(max_char_var)
- class FormattedValueNode(ExprNode):
- # {}-delimited portions of an f-string
- #
- # value ExprNode The expression itself
- # conversion_char str or None Type conversion (!s, !r, !a, or none, or 'd' for integer conversion)
- # format_spec JoinedStrNode or None Format string passed to __format__
- # c_format_spec str or None If not None, formatting can be done at the C level
- subexprs = ['value', 'format_spec']
- type = unicode_type
- is_temp = True
- c_format_spec = None
- find_conversion_func = {
- 's': 'PyObject_Unicode',
- 'r': 'PyObject_Repr',
- 'a': 'PyObject_ASCII', # NOTE: mapped to PyObject_Repr() in Py2
- 'd': '__Pyx_PyNumber_IntOrLong', # NOTE: internal mapping for '%d' formatting
- }.get
- def may_be_none(self):
- # PyObject_Format() always returns a Unicode string or raises an exception
- return False
- def analyse_types(self, env):
- self.value = self.value.analyse_types(env)
- if not self.format_spec or self.format_spec.is_string_literal:
- c_format_spec = self.format_spec.value if self.format_spec else self.value.type.default_format_spec
- if self.value.type.can_coerce_to_pystring(env, format_spec=c_format_spec):
- self.c_format_spec = c_format_spec
- if self.format_spec:
- self.format_spec = self.format_spec.analyse_types(env).coerce_to_pyobject(env)
- if self.c_format_spec is None:
- self.value = self.value.coerce_to_pyobject(env)
- if not self.format_spec and (not self.conversion_char or self.conversion_char == 's'):
- if self.value.type is unicode_type and not self.value.may_be_none():
- # value is definitely a unicode string and we don't format it any special
- return self.value
- return self
- def generate_result_code(self, code):
- if self.c_format_spec is not None and not self.value.type.is_pyobject:
- convert_func_call = self.value.type.convert_to_pystring(
- self.value.result(), code, self.c_format_spec)
- code.putln("%s = %s; %s" % (
- self.result(),
- convert_func_call,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- return
- value_result = self.value.py_result()
- value_is_unicode = self.value.type is unicode_type and not self.value.may_be_none()
- if self.format_spec:
- format_func = '__Pyx_PyObject_Format'
- format_spec = self.format_spec.py_result()
- else:
- # common case: expect simple Unicode pass-through if no format spec
- format_func = '__Pyx_PyObject_FormatSimple'
- # passing a Unicode format string in Py2 forces PyObject_Format() to also return a Unicode string
- format_spec = Naming.empty_unicode
- conversion_char = self.conversion_char
- if conversion_char == 's' and value_is_unicode:
- # no need to pipe unicode strings through str()
- conversion_char = None
- if conversion_char:
- fn = self.find_conversion_func(conversion_char)
- assert fn is not None, "invalid conversion character found: '%s'" % conversion_char
- value_result = '%s(%s)' % (fn, value_result)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectFormatAndDecref", "StringTools.c"))
- format_func += 'AndDecref'
- elif self.format_spec:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectFormat", "StringTools.c"))
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectFormatSimple", "StringTools.c"))
- code.putln("%s = %s(%s, %s); %s" % (
- self.result(),
- format_func,
- value_result,
- format_spec,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- #-------------------------------------------------------------------
- #
- # Parallel nodes (cython.parallel.thread(savailable|id))
- #
- #-------------------------------------------------------------------
- class ParallelThreadsAvailableNode(AtomicExprNode):
- """
- Note: this is disabled and not a valid directive at this moment
- Implements cython.parallel.threadsavailable(). If we are called from the
- sequential part of the application, we need to call omp_get_max_threads(),
- and in the parallel part we can just call omp_get_num_threads()
- """
- type = PyrexTypes.c_int_type
- def analyse_types(self, env):
- self.is_temp = True
- # env.add_include_file("omp.h")
- return self
- def generate_result_code(self, code):
- code.putln("#ifdef _OPENMP")
- code.putln("if (omp_in_parallel()) %s = omp_get_max_threads();" %
- self.temp_code)
- code.putln("else %s = omp_get_num_threads();" % self.temp_code)
- code.putln("#else")
- code.putln("%s = 1;" % self.temp_code)
- code.putln("#endif")
- def result(self):
- return self.temp_code
- class ParallelThreadIdNode(AtomicExprNode): #, Nodes.ParallelNode):
- """
- Implements cython.parallel.threadid()
- """
- type = PyrexTypes.c_int_type
- def analyse_types(self, env):
- self.is_temp = True
- # env.add_include_file("omp.h")
- return self
- def generate_result_code(self, code):
- code.putln("#ifdef _OPENMP")
- code.putln("%s = omp_get_thread_num();" % self.temp_code)
- code.putln("#else")
- code.putln("%s = 0;" % self.temp_code)
- code.putln("#endif")
- def result(self):
- return self.temp_code
- #-------------------------------------------------------------------
- #
- # Trailer nodes
- #
- #-------------------------------------------------------------------
- class _IndexingBaseNode(ExprNode):
- # Base class for indexing nodes.
- #
- # base ExprNode the value being indexed
- def is_ephemeral(self):
- # in most cases, indexing will return a safe reference to an object in a container,
- # so we consider the result safe if the base object is
- return self.base.is_ephemeral() or self.base.type in (
- basestring_type, str_type, bytes_type, bytearray_type, unicode_type)
- def check_const_addr(self):
- return self.base.check_const_addr() and self.index.check_const()
- def is_lvalue(self):
- # NOTE: references currently have both is_reference and is_ptr
- # set. Since pointers and references have different lvalue
- # rules, we must be careful to separate the two.
- if self.type.is_reference:
- if self.type.ref_base_type.is_array:
- # fixed-sized arrays aren't l-values
- return False
- elif self.type.is_ptr:
- # non-const pointers can always be reassigned
- return True
- # Just about everything else returned by the index operator
- # can be an lvalue.
- return True
- class IndexNode(_IndexingBaseNode):
- # Sequence indexing.
- #
- # base ExprNode
- # index ExprNode
- # type_indices [PyrexType]
- #
- # is_fused_index boolean Whether the index is used to specialize a
- # c(p)def function
- subexprs = ['base', 'index']
- type_indices = None
- is_subscript = True
- is_fused_index = False
- def calculate_constant_result(self):
- self.constant_result = self.base.constant_result[self.index.constant_result]
- def compile_time_value(self, denv):
- base = self.base.compile_time_value(denv)
- index = self.index.compile_time_value(denv)
- try:
- return base[index]
- except Exception as e:
- self.compile_time_value_error(e)
- def is_simple(self):
- base = self.base
- return (base.is_simple() and self.index.is_simple()
- and base.type and (base.type.is_ptr or base.type.is_array))
- def may_be_none(self):
- base_type = self.base.type
- if base_type:
- if base_type.is_string:
- return False
- if isinstance(self.index, SliceNode):
- # slicing!
- if base_type in (bytes_type, bytearray_type, str_type, unicode_type,
- basestring_type, list_type, tuple_type):
- return False
- return ExprNode.may_be_none(self)
- def analyse_target_declaration(self, env):
- pass
- def analyse_as_type(self, env):
- base_type = self.base.analyse_as_type(env)
- if base_type and not base_type.is_pyobject:
- if base_type.is_cpp_class:
- if isinstance(self.index, TupleNode):
- template_values = self.index.args
- else:
- template_values = [self.index]
- type_node = Nodes.TemplatedTypeNode(
- pos=self.pos,
- positional_args=template_values,
- keyword_args=None)
- return type_node.analyse(env, base_type=base_type)
- elif self.index.is_slice or self.index.is_sequence_constructor:
- # memory view
- from . import MemoryView
- env.use_utility_code(MemoryView.view_utility_code)
- axes = [self.index] if self.index.is_slice else list(self.index.args)
- return PyrexTypes.MemoryViewSliceType(base_type, MemoryView.get_axes_specs(env, axes))
- else:
- # C array
- index = self.index.compile_time_value(env)
- if index is not None:
- try:
- index = int(index)
- except (ValueError, TypeError):
- pass
- else:
- return PyrexTypes.CArrayType(base_type, index)
- error(self.pos, "Array size must be a compile time constant")
- return None
- def type_dependencies(self, env):
- return self.base.type_dependencies(env) + self.index.type_dependencies(env)
- def infer_type(self, env):
- base_type = self.base.infer_type(env)
- if self.index.is_slice:
- # slicing!
- if base_type.is_string:
- # sliced C strings must coerce to Python
- return bytes_type
- elif base_type.is_pyunicode_ptr:
- # sliced Py_UNICODE* strings must coerce to Python
- return unicode_type
- elif base_type in (unicode_type, bytes_type, str_type,
- bytearray_type, list_type, tuple_type):
- # slicing these returns the same type
- return base_type
- else:
- # TODO: Handle buffers (hopefully without too much redundancy).
- return py_object_type
- index_type = self.index.infer_type(env)
- if index_type and index_type.is_int or isinstance(self.index, IntNode):
- # indexing!
- if base_type is unicode_type:
- # Py_UCS4 will automatically coerce to a unicode string
- # if required, so this is safe. We only infer Py_UCS4
- # when the index is a C integer type. Otherwise, we may
- # need to use normal Python item access, in which case
- # it's faster to return the one-char unicode string than
- # to receive it, throw it away, and potentially rebuild it
- # on a subsequent PyObject coercion.
- return PyrexTypes.c_py_ucs4_type
- elif base_type is str_type:
- # always returns str - Py2: bytes, Py3: unicode
- return base_type
- elif base_type is bytearray_type:
- return PyrexTypes.c_uchar_type
- elif isinstance(self.base, BytesNode):
- #if env.global_scope().context.language_level >= 3:
- # # inferring 'char' can be made to work in Python 3 mode
- # return PyrexTypes.c_char_type
- # Py2/3 return different types on indexing bytes objects
- return py_object_type
- elif base_type in (tuple_type, list_type):
- # if base is a literal, take a look at its values
- item_type = infer_sequence_item_type(
- env, self.base, self.index, seq_type=base_type)
- if item_type is not None:
- return item_type
- elif base_type.is_ptr or base_type.is_array:
- return base_type.base_type
- elif base_type.is_ctuple and isinstance(self.index, IntNode):
- if self.index.has_constant_result():
- index = self.index.constant_result
- if index < 0:
- index += base_type.size
- if 0 <= index < base_type.size:
- return base_type.components[index]
- if base_type.is_cpp_class:
- class FakeOperand:
- def __init__(self, **kwds):
- self.__dict__.update(kwds)
- operands = [
- FakeOperand(pos=self.pos, type=base_type),
- FakeOperand(pos=self.pos, type=index_type),
- ]
- index_func = env.lookup_operator('[]', operands)
- if index_func is not None:
- return index_func.type.return_type
- if is_pythran_expr(base_type) and is_pythran_expr(index_type):
- index_with_type = (self.index, index_type)
- return PythranExpr(pythran_indexing_type(base_type, [index_with_type]))
- # may be slicing or indexing, we don't know
- if base_type in (unicode_type, str_type):
- # these types always returns their own type on Python indexing/slicing
- return base_type
- else:
- # TODO: Handle buffers (hopefully without too much redundancy).
- return py_object_type
- def analyse_types(self, env):
- return self.analyse_base_and_index_types(env, getting=True)
- def analyse_target_types(self, env):
- node = self.analyse_base_and_index_types(env, setting=True)
- if node.type.is_const:
- error(self.pos, "Assignment to const dereference")
- if node is self and not node.is_lvalue():
- error(self.pos, "Assignment to non-lvalue of type '%s'" % node.type)
- return node
- def analyse_base_and_index_types(self, env, getting=False, setting=False,
- analyse_base=True):
- # Note: This might be cleaned up by having IndexNode
- # parsed in a saner way and only construct the tuple if
- # needed.
- if analyse_base:
- self.base = self.base.analyse_types(env)
- if self.base.type.is_error:
- # Do not visit child tree if base is undeclared to avoid confusing
- # error messages
- self.type = PyrexTypes.error_type
- return self
- is_slice = self.index.is_slice
- if not env.directives['wraparound']:
- if is_slice:
- check_negative_indices(self.index.start, self.index.stop)
- else:
- check_negative_indices(self.index)
- # Potentially overflowing index value.
- if not is_slice and isinstance(self.index, IntNode) and Utils.long_literal(self.index.value):
- self.index = self.index.coerce_to_pyobject(env)
- is_memslice = self.base.type.is_memoryviewslice
- # Handle the case where base is a literal char* (and we expect a string, not an int)
- if not is_memslice and (isinstance(self.base, BytesNode) or is_slice):
- if self.base.type.is_string or not (self.base.type.is_ptr or self.base.type.is_array):
- self.base = self.base.coerce_to_pyobject(env)
- replacement_node = self.analyse_as_buffer_operation(env, getting)
- if replacement_node is not None:
- return replacement_node
- self.nogil = env.nogil
- base_type = self.base.type
- if not base_type.is_cfunction:
- self.index = self.index.analyse_types(env)
- self.original_index_type = self.index.type
- if base_type.is_unicode_char:
- # we infer Py_UNICODE/Py_UCS4 for unicode strings in some
- # cases, but indexing must still work for them
- if setting:
- warning(self.pos, "cannot assign to Unicode string index", level=1)
- elif self.index.constant_result in (0, -1):
- # uchar[0] => uchar
- return self.base
- self.base = self.base.coerce_to_pyobject(env)
- base_type = self.base.type
- if base_type.is_pyobject:
- return self.analyse_as_pyobject(env, is_slice, getting, setting)
- elif base_type.is_ptr or base_type.is_array:
- return self.analyse_as_c_array(env, is_slice)
- elif base_type.is_cpp_class:
- return self.analyse_as_cpp(env, setting)
- elif base_type.is_cfunction:
- return self.analyse_as_c_function(env)
- elif base_type.is_ctuple:
- return self.analyse_as_c_tuple(env, getting, setting)
- else:
- error(self.pos,
- "Attempting to index non-array type '%s'" %
- base_type)
- self.type = PyrexTypes.error_type
- return self
- def analyse_as_pyobject(self, env, is_slice, getting, setting):
- base_type = self.base.type
- if self.index.type.is_unicode_char and base_type is not dict_type:
- # TODO: eventually fold into case below and remove warning, once people have adapted their code
- warning(self.pos,
- "Item lookup of unicode character codes now always converts to a Unicode string. "
- "Use an explicit C integer cast to get back the previous integer lookup behaviour.", level=1)
- self.index = self.index.coerce_to_pyobject(env)
- self.is_temp = 1
- elif self.index.type.is_int and base_type is not dict_type:
- if (getting
- and (base_type in (list_type, tuple_type, bytearray_type))
- and (not self.index.type.signed
- or not env.directives['wraparound']
- or (isinstance(self.index, IntNode) and
- self.index.has_constant_result() and self.index.constant_result >= 0))
- and not env.directives['boundscheck']):
- self.is_temp = 0
- else:
- self.is_temp = 1
- self.index = self.index.coerce_to(PyrexTypes.c_py_ssize_t_type, env).coerce_to_simple(env)
- self.original_index_type.create_to_py_utility_code(env)
- else:
- self.index = self.index.coerce_to_pyobject(env)
- self.is_temp = 1
- if self.index.type.is_int and base_type is unicode_type:
- # Py_UNICODE/Py_UCS4 will automatically coerce to a unicode string
- # if required, so this is fast and safe
- self.type = PyrexTypes.c_py_ucs4_type
- elif self.index.type.is_int and base_type is bytearray_type:
- if setting:
- self.type = PyrexTypes.c_uchar_type
- else:
- # not using 'uchar' to enable fast and safe error reporting as '-1'
- self.type = PyrexTypes.c_int_type
- elif is_slice and base_type in (bytes_type, bytearray_type, str_type, unicode_type, list_type, tuple_type):
- self.type = base_type
- else:
- item_type = None
- if base_type in (list_type, tuple_type) and self.index.type.is_int:
- item_type = infer_sequence_item_type(
- env, self.base, self.index, seq_type=base_type)
- if item_type is None:
- item_type = py_object_type
- self.type = item_type
- if base_type in (list_type, tuple_type, dict_type):
- # do the None check explicitly (not in a helper) to allow optimising it away
- self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
- self.wrap_in_nonecheck_node(env, getting)
- return self
- def analyse_as_c_array(self, env, is_slice):
- base_type = self.base.type
- self.type = base_type.base_type
- if is_slice:
- self.type = base_type
- elif self.index.type.is_pyobject:
- self.index = self.index.coerce_to(PyrexTypes.c_py_ssize_t_type, env)
- elif not self.index.type.is_int:
- error(self.pos, "Invalid index type '%s'" % self.index.type)
- return self
- def analyse_as_cpp(self, env, setting):
- base_type = self.base.type
- function = env.lookup_operator("[]", [self.base, self.index])
- if function is None:
- error(self.pos, "Indexing '%s' not supported for index type '%s'" % (base_type, self.index.type))
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return self
- func_type = function.type
- if func_type.is_ptr:
- func_type = func_type.base_type
- self.exception_check = func_type.exception_check
- self.exception_value = func_type.exception_value
- if self.exception_check:
- if not setting:
- self.is_temp = True
- if self.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- self.index = self.index.coerce_to(func_type.args[0].type, env)
- self.type = func_type.return_type
- if setting and not func_type.return_type.is_reference:
- error(self.pos, "Can't set non-reference result '%s'" % self.type)
- return self
- def analyse_as_c_function(self, env):
- base_type = self.base.type
- if base_type.is_fused:
- self.parse_indexed_fused_cdef(env)
- else:
- self.type_indices = self.parse_index_as_types(env)
- self.index = None # FIXME: use a dedicated Node class instead of generic IndexNode
- if base_type.templates is None:
- error(self.pos, "Can only parameterize template functions.")
- self.type = error_type
- elif self.type_indices is None:
- # Error recorded earlier.
- self.type = error_type
- elif len(base_type.templates) != len(self.type_indices):
- error(self.pos, "Wrong number of template arguments: expected %s, got %s" % (
- (len(base_type.templates), len(self.type_indices))))
- self.type = error_type
- else:
- self.type = base_type.specialize(dict(zip(base_type.templates, self.type_indices)))
- # FIXME: use a dedicated Node class instead of generic IndexNode
- return self
- def analyse_as_c_tuple(self, env, getting, setting):
- base_type = self.base.type
- if isinstance(self.index, IntNode) and self.index.has_constant_result():
- index = self.index.constant_result
- if -base_type.size <= index < base_type.size:
- if index < 0:
- index += base_type.size
- self.type = base_type.components[index]
- else:
- error(self.pos,
- "Index %s out of bounds for '%s'" %
- (index, base_type))
- self.type = PyrexTypes.error_type
- return self
- else:
- self.base = self.base.coerce_to_pyobject(env)
- return self.analyse_base_and_index_types(env, getting=getting, setting=setting, analyse_base=False)
- def analyse_as_buffer_operation(self, env, getting):
- """
- Analyse buffer indexing and memoryview indexing/slicing
- """
- if isinstance(self.index, TupleNode):
- indices = self.index.args
- else:
- indices = [self.index]
- base = self.base
- base_type = base.type
- replacement_node = None
- if base_type.is_memoryviewslice:
- # memoryviewslice indexing or slicing
- from . import MemoryView
- if base.is_memview_slice:
- # For memory views, "view[i][j]" is the same as "view[i, j]" => use the latter for speed.
- merged_indices = base.merged_indices(indices)
- if merged_indices is not None:
- base = base.base
- base_type = base.type
- indices = merged_indices
- have_slices, indices, newaxes = MemoryView.unellipsify(indices, base_type.ndim)
- if have_slices:
- replacement_node = MemoryViewSliceNode(self.pos, indices=indices, base=base)
- else:
- replacement_node = MemoryViewIndexNode(self.pos, indices=indices, base=base)
- elif base_type.is_buffer or base_type.is_pythran_expr:
- if base_type.is_pythran_expr or len(indices) == base_type.ndim:
- # Buffer indexing
- is_buffer_access = True
- indices = [index.analyse_types(env) for index in indices]
- if base_type.is_pythran_expr:
- do_replacement = all(
- index.type.is_int or index.is_slice or index.type.is_pythran_expr
- for index in indices)
- if do_replacement:
- for i,index in enumerate(indices):
- if index.is_slice:
- index = SliceIntNode(index.pos, start=index.start, stop=index.stop, step=index.step)
- index = index.analyse_types(env)
- indices[i] = index
- else:
- do_replacement = all(index.type.is_int for index in indices)
- if do_replacement:
- replacement_node = BufferIndexNode(self.pos, indices=indices, base=base)
- # On cloning, indices is cloned. Otherwise, unpack index into indices.
- assert not isinstance(self.index, CloneNode)
- if replacement_node is not None:
- replacement_node = replacement_node.analyse_types(env, getting)
- return replacement_node
- def wrap_in_nonecheck_node(self, env, getting):
- if not env.directives['nonecheck'] or not self.base.may_be_none():
- return
- self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
- def parse_index_as_types(self, env, required=True):
- if isinstance(self.index, TupleNode):
- indices = self.index.args
- else:
- indices = [self.index]
- type_indices = []
- for index in indices:
- type_indices.append(index.analyse_as_type(env))
- if type_indices[-1] is None:
- if required:
- error(index.pos, "not parsable as a type")
- return None
- return type_indices
- def parse_indexed_fused_cdef(self, env):
- """
- Interpret fused_cdef_func[specific_type1, ...]
- Note that if this method is called, we are an indexed cdef function
- with fused argument types, and this IndexNode will be replaced by the
- NameNode with specific entry just after analysis of expressions by
- AnalyseExpressionsTransform.
- """
- self.type = PyrexTypes.error_type
- self.is_fused_index = True
- base_type = self.base.type
- positions = []
- if self.index.is_name or self.index.is_attribute:
- positions.append(self.index.pos)
- elif isinstance(self.index, TupleNode):
- for arg in self.index.args:
- positions.append(arg.pos)
- specific_types = self.parse_index_as_types(env, required=False)
- if specific_types is None:
- self.index = self.index.analyse_types(env)
- if not self.base.entry.as_variable:
- error(self.pos, "Can only index fused functions with types")
- else:
- # A cpdef function indexed with Python objects
- self.base.entry = self.entry = self.base.entry.as_variable
- self.base.type = self.type = self.entry.type
- self.base.is_temp = True
- self.is_temp = True
- self.entry.used = True
- self.is_fused_index = False
- return
- for i, type in enumerate(specific_types):
- specific_types[i] = type.specialize_fused(env)
- fused_types = base_type.get_fused_types()
- if len(specific_types) > len(fused_types):
- return error(self.pos, "Too many types specified")
- elif len(specific_types) < len(fused_types):
- t = fused_types[len(specific_types)]
- return error(self.pos, "Not enough types specified to specialize "
- "the function, %s is still fused" % t)
- # See if our index types form valid specializations
- for pos, specific_type, fused_type in zip(positions,
- specific_types,
- fused_types):
- if not any([specific_type.same_as(t) for t in fused_type.types]):
- return error(pos, "Type not in fused type")
- if specific_type is None or specific_type.is_error:
- return
- fused_to_specific = dict(zip(fused_types, specific_types))
- type = base_type.specialize(fused_to_specific)
- if type.is_fused:
- # Only partially specific, this is invalid
- error(self.pos,
- "Index operation makes function only partially specific")
- else:
- # Fully specific, find the signature with the specialized entry
- for signature in self.base.type.get_all_specialized_function_types():
- if type.same_as(signature):
- self.type = signature
- if self.base.is_attribute:
- # Pretend to be a normal attribute, for cdef extension
- # methods
- self.entry = signature.entry
- self.is_attribute = True
- self.obj = self.base.obj
- self.type.entry.used = True
- self.base.type = signature
- self.base.entry = signature.entry
- break
- else:
- # This is a bug
- raise InternalError("Couldn't find the right signature")
- gil_message = "Indexing Python object"
- def calculate_result_code(self):
- if self.base.type in (list_type, tuple_type, bytearray_type):
- if self.base.type is list_type:
- index_code = "PyList_GET_ITEM(%s, %s)"
- elif self.base.type is tuple_type:
- index_code = "PyTuple_GET_ITEM(%s, %s)"
- elif self.base.type is bytearray_type:
- index_code = "((unsigned char)(PyByteArray_AS_STRING(%s)[%s]))"
- else:
- assert False, "unexpected base type in indexing: %s" % self.base.type
- elif self.base.type.is_cfunction:
- return "%s<%s>" % (
- self.base.result(),
- ",".join([param.empty_declaration_code() for param in self.type_indices]))
- elif self.base.type.is_ctuple:
- index = self.index.constant_result
- if index < 0:
- index += self.base.type.size
- return "%s.f%s" % (self.base.result(), index)
- else:
- if (self.type.is_ptr or self.type.is_array) and self.type == self.base.type:
- error(self.pos, "Invalid use of pointer slice")
- return
- index_code = "(%s[%s])"
- return index_code % (self.base.result(), self.index.result())
- def extra_index_params(self, code):
- if self.index.type.is_int:
- is_list = self.base.type is list_type
- wraparound = (
- bool(code.globalstate.directives['wraparound']) and
- self.original_index_type.signed and
- not (isinstance(self.index.constant_result, _py_int_types)
- and self.index.constant_result >= 0))
- boundscheck = bool(code.globalstate.directives['boundscheck'])
- return ", %s, %d, %s, %d, %d, %d" % (
- self.original_index_type.empty_declaration_code(),
- self.original_index_type.signed and 1 or 0,
- self.original_index_type.to_py_function,
- is_list, wraparound, boundscheck)
- else:
- return ""
- def generate_result_code(self, code):
- if not self.is_temp:
- # all handled in self.calculate_result_code()
- return
- utility_code = None
- if self.type.is_pyobject:
- error_value = 'NULL'
- if self.index.type.is_int:
- if self.base.type is list_type:
- function = "__Pyx_GetItemInt_List"
- elif self.base.type is tuple_type:
- function = "__Pyx_GetItemInt_Tuple"
- else:
- function = "__Pyx_GetItemInt"
- utility_code = TempitaUtilityCode.load_cached("GetItemInt", "ObjectHandling.c")
- else:
- if self.base.type is dict_type:
- function = "__Pyx_PyDict_GetItem"
- utility_code = UtilityCode.load_cached("DictGetItem", "ObjectHandling.c")
- elif self.base.type is py_object_type and self.index.type in (str_type, unicode_type):
- # obj[str] is probably doing a dict lookup
- function = "__Pyx_PyObject_Dict_GetItem"
- utility_code = UtilityCode.load_cached("DictGetItem", "ObjectHandling.c")
- else:
- function = "__Pyx_PyObject_GetItem"
- code.globalstate.use_utility_code(
- TempitaUtilityCode.load_cached("GetItemInt", "ObjectHandling.c"))
- utility_code = UtilityCode.load_cached("ObjectGetItem", "ObjectHandling.c")
- elif self.type.is_unicode_char and self.base.type is unicode_type:
- assert self.index.type.is_int
- function = "__Pyx_GetItemInt_Unicode"
- error_value = '(Py_UCS4)-1'
- utility_code = UtilityCode.load_cached("GetItemIntUnicode", "StringTools.c")
- elif self.base.type is bytearray_type:
- assert self.index.type.is_int
- assert self.type.is_int
- function = "__Pyx_GetItemInt_ByteArray"
- error_value = '-1'
- utility_code = UtilityCode.load_cached("GetItemIntByteArray", "StringTools.c")
- elif not (self.base.type.is_cpp_class and self.exception_check):
- assert False, "unexpected type %s and base type %s for indexing" % (
- self.type, self.base.type)
- if utility_code is not None:
- code.globalstate.use_utility_code(utility_code)
- if self.index.type.is_int:
- index_code = self.index.result()
- else:
- index_code = self.index.py_result()
- if self.base.type.is_cpp_class and self.exception_check:
- translate_cpp_exception(code, self.pos,
- "%s = %s[%s];" % (self.result(), self.base.result(),
- self.index.result()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- else:
- error_check = '!%s' if error_value == 'NULL' else '%%s == %s' % error_value
- code.putln(
- "%s = %s(%s, %s%s); %s" % (
- self.result(),
- function,
- self.base.py_result(),
- index_code,
- self.extra_index_params(code),
- code.error_goto_if(error_check % self.result(), self.pos)))
- if self.type.is_pyobject:
- code.put_gotref(self.py_result())
- def generate_setitem_code(self, value_code, code):
- if self.index.type.is_int:
- if self.base.type is bytearray_type:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("SetItemIntByteArray", "StringTools.c"))
- function = "__Pyx_SetItemInt_ByteArray"
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("SetItemInt", "ObjectHandling.c"))
- function = "__Pyx_SetItemInt"
- index_code = self.index.result()
- else:
- index_code = self.index.py_result()
- if self.base.type is dict_type:
- function = "PyDict_SetItem"
- # It would seem that we could specialized lists/tuples, but that
- # shouldn't happen here.
- # Both PyList_SetItem() and PyTuple_SetItem() take a Py_ssize_t as
- # index instead of an object, and bad conversion here would give
- # the wrong exception. Also, tuples are supposed to be immutable,
- # and raise a TypeError when trying to set their entries
- # (PyTuple_SetItem() is for creating new tuples from scratch).
- else:
- function = "PyObject_SetItem"
- code.putln(code.error_goto_if_neg(
- "%s(%s, %s, %s%s)" % (
- function,
- self.base.py_result(),
- index_code,
- value_code,
- self.extra_index_params(code)),
- self.pos))
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- self.generate_subexpr_evaluation_code(code)
- if self.type.is_pyobject:
- self.generate_setitem_code(rhs.py_result(), code)
- elif self.base.type is bytearray_type:
- value_code = self._check_byte_value(code, rhs)
- self.generate_setitem_code(value_code, code)
- elif self.base.type.is_cpp_class and self.exception_check and self.exception_check == '+':
- if overloaded_assignment and exception_check and \
- self.exception_value != exception_value:
- # Handle the case that both the index operator and the assignment
- # operator have a c++ exception handler and they are not the same.
- translate_double_cpp_exception(code, self.pos, self.type,
- self.result(), rhs.result(), self.exception_value,
- exception_value, self.in_nogil_context)
- else:
- # Handle the case that only the index operator has a
- # c++ exception handler, or that
- # both exception handlers are the same.
- translate_cpp_exception(code, self.pos,
- "%s = %s;" % (self.result(), rhs.result()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- else:
- code.putln(
- "%s = %s;" % (self.result(), rhs.result()))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- def _check_byte_value(self, code, rhs):
- # TODO: should we do this generally on downcasts, or just here?
- assert rhs.type.is_int, repr(rhs.type)
- value_code = rhs.result()
- if rhs.has_constant_result():
- if 0 <= rhs.constant_result < 256:
- return value_code
- needs_cast = True # make at least the C compiler happy
- warning(rhs.pos,
- "value outside of range(0, 256)"
- " when assigning to byte: %s" % rhs.constant_result,
- level=1)
- else:
- needs_cast = rhs.type != PyrexTypes.c_uchar_type
- if not self.nogil:
- conditions = []
- if rhs.is_literal or rhs.type.signed:
- conditions.append('%s < 0' % value_code)
- if (rhs.is_literal or not
- (rhs.is_temp and rhs.type in (
- PyrexTypes.c_uchar_type, PyrexTypes.c_char_type,
- PyrexTypes.c_schar_type))):
- conditions.append('%s > 255' % value_code)
- if conditions:
- code.putln("if (unlikely(%s)) {" % ' || '.join(conditions))
- code.putln(
- 'PyErr_SetString(PyExc_ValueError,'
- ' "byte must be in range(0, 256)"); %s' %
- code.error_goto(self.pos))
- code.putln("}")
- if needs_cast:
- value_code = '((unsigned char)%s)' % value_code
- return value_code
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- self.generate_subexpr_evaluation_code(code)
- #if self.type.is_pyobject:
- if self.index.type.is_int:
- function = "__Pyx_DelItemInt"
- index_code = self.index.result()
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("DelItemInt", "ObjectHandling.c"))
- else:
- index_code = self.index.py_result()
- if self.base.type is dict_type:
- function = "PyDict_DelItem"
- else:
- function = "PyObject_DelItem"
- code.putln(code.error_goto_if_neg(
- "%s(%s, %s%s)" % (
- function,
- self.base.py_result(),
- index_code,
- self.extra_index_params(code)),
- self.pos))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- class BufferIndexNode(_IndexingBaseNode):
- """
- Indexing of buffers and memoryviews. This node is created during type
- analysis from IndexNode and replaces it.
- Attributes:
- base - base node being indexed
- indices - list of indexing expressions
- """
- subexprs = ['base', 'indices']
- is_buffer_access = True
- # Whether we're assigning to a buffer (in that case it needs to be writable)
- writable_needed = False
- # Any indexing temp variables that we need to clean up.
- index_temps = ()
- def analyse_target_types(self, env):
- self.analyse_types(env, getting=False)
- def analyse_types(self, env, getting=True):
- """
- Analyse types for buffer indexing only. Overridden by memoryview
- indexing and slicing subclasses
- """
- # self.indices are already analyzed
- if not self.base.is_name and not is_pythran_expr(self.base.type):
- error(self.pos, "Can only index buffer variables")
- self.type = error_type
- return self
- if not getting:
- if not self.base.entry.type.writable:
- error(self.pos, "Writing to readonly buffer")
- else:
- self.writable_needed = True
- if self.base.type.is_buffer:
- self.base.entry.buffer_aux.writable_needed = True
- self.none_error_message = "'NoneType' object is not subscriptable"
- self.analyse_buffer_index(env, getting)
- self.wrap_in_nonecheck_node(env)
- return self
- def analyse_buffer_index(self, env, getting):
- if is_pythran_expr(self.base.type):
- index_with_type_list = [(idx, idx.type) for idx in self.indices]
- self.type = PythranExpr(pythran_indexing_type(self.base.type, index_with_type_list))
- else:
- self.base = self.base.coerce_to_simple(env)
- self.type = self.base.type.dtype
- self.buffer_type = self.base.type
- if getting and (self.type.is_pyobject or self.type.is_pythran_expr):
- self.is_temp = True
- def analyse_assignment(self, rhs):
- """
- Called by IndexNode when this node is assigned to,
- with the rhs of the assignment
- """
- def wrap_in_nonecheck_node(self, env):
- if not env.directives['nonecheck'] or not self.base.may_be_none():
- return
- self.base = self.base.as_none_safe_node(self.none_error_message)
- def nogil_check(self, env):
- if self.is_buffer_access or self.is_memview_index:
- if self.type.is_pyobject:
- error(self.pos, "Cannot access buffer with object dtype without gil")
- self.type = error_type
- def calculate_result_code(self):
- return "(*%s)" % self.buffer_ptr_code
- def buffer_entry(self):
- base = self.base
- if self.base.is_nonecheck:
- base = base.arg
- return base.type.get_entry(base)
- def get_index_in_temp(self, code, ivar):
- ret = code.funcstate.allocate_temp(
- PyrexTypes.widest_numeric_type(
- ivar.type,
- PyrexTypes.c_ssize_t_type if ivar.type.signed else PyrexTypes.c_size_t_type),
- manage_ref=False)
- code.putln("%s = %s;" % (ret, ivar.result()))
- return ret
- def buffer_lookup_code(self, code):
- """
- ndarray[1, 2, 3] and memslice[1, 2, 3]
- """
- if self.in_nogil_context:
- if self.is_buffer_access or self.is_memview_index:
- if code.globalstate.directives['boundscheck']:
- warning(self.pos, "Use boundscheck(False) for faster access", level=1)
- # Assign indices to temps of at least (s)size_t to allow further index calculations.
- self.index_temps = index_temps = [self.get_index_in_temp(code,ivar) for ivar in self.indices]
- # Generate buffer access code using these temps
- from . import Buffer
- buffer_entry = self.buffer_entry()
- if buffer_entry.type.is_buffer:
- negative_indices = buffer_entry.type.negative_indices
- else:
- negative_indices = Buffer.buffer_defaults['negative_indices']
- return buffer_entry, Buffer.put_buffer_lookup_code(
- entry=buffer_entry,
- index_signeds=[ivar.type.signed for ivar in self.indices],
- index_cnames=index_temps,
- directives=code.globalstate.directives,
- pos=self.pos, code=code,
- negative_indices=negative_indices,
- in_nogil_context=self.in_nogil_context)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
- self.generate_subexpr_evaluation_code(code)
- self.generate_buffer_setitem_code(rhs, code)
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- def generate_buffer_setitem_code(self, rhs, code, op=""):
- base_type = self.base.type
- if is_pythran_expr(base_type) and is_pythran_supported_type(rhs.type):
- obj = code.funcstate.allocate_temp(PythranExpr(pythran_type(self.base.type)), manage_ref=False)
- # We have got to do this because we have to declare pythran objects
- # at the beginning of the functions.
- # Indeed, Cython uses "goto" statement for error management, and
- # RAII doesn't work with that kind of construction.
- # Moreover, the way Pythran expressions are made is that they don't
- # support move-assignation easily.
- # This, we explicitly destroy then in-place new objects in this
- # case.
- code.putln("__Pyx_call_destructor(%s);" % obj)
- code.putln("new (&%s) decltype(%s){%s};" % (obj, obj, self.base.pythran_result()))
- code.putln("%s%s %s= %s;" % (
- obj,
- pythran_indexing_code(self.indices),
- op,
- rhs.pythran_result()))
- code.funcstate.release_temp(obj)
- return
- # Used from generate_assignment_code and InPlaceAssignmentNode
- buffer_entry, ptrexpr = self.buffer_lookup_code(code)
- if self.buffer_type.dtype.is_pyobject:
- # Must manage refcounts. Decref what is already there
- # and incref what we put in.
- ptr = code.funcstate.allocate_temp(buffer_entry.buf_ptr_type,
- manage_ref=False)
- rhs_code = rhs.result()
- code.putln("%s = %s;" % (ptr, ptrexpr))
- code.put_gotref("*%s" % ptr)
- code.putln("__Pyx_INCREF(%s); __Pyx_DECREF(*%s);" % (
- rhs_code, ptr))
- code.putln("*%s %s= %s;" % (ptr, op, rhs_code))
- code.put_giveref("*%s" % ptr)
- code.funcstate.release_temp(ptr)
- else:
- # Simple case
- code.putln("*%s %s= %s;" % (ptrexpr, op, rhs.result()))
- def generate_result_code(self, code):
- if is_pythran_expr(self.base.type):
- res = self.result()
- code.putln("__Pyx_call_destructor(%s);" % res)
- code.putln("new (&%s) decltype(%s){%s%s};" % (
- res,
- res,
- self.base.pythran_result(),
- pythran_indexing_code(self.indices)))
- return
- buffer_entry, self.buffer_ptr_code = self.buffer_lookup_code(code)
- if self.type.is_pyobject:
- # is_temp is True, so must pull out value and incref it.
- # NOTE: object temporary results for nodes are declared
- # as PyObject *, so we need a cast
- code.putln("%s = (PyObject *) *%s;" % (self.result(), self.buffer_ptr_code))
- code.putln("__Pyx_INCREF((PyObject*)%s);" % self.result())
- def free_subexpr_temps(self, code):
- for temp in self.index_temps:
- code.funcstate.release_temp(temp)
- self.index_temps = ()
- super(BufferIndexNode, self).free_subexpr_temps(code)
- class MemoryViewIndexNode(BufferIndexNode):
- is_memview_index = True
- is_buffer_access = False
- warned_untyped_idx = False
- def analyse_types(self, env, getting=True):
- # memoryviewslice indexing or slicing
- from . import MemoryView
- self.is_pythran_mode = has_np_pythran(env)
- indices = self.indices
- have_slices, indices, newaxes = MemoryView.unellipsify(indices, self.base.type.ndim)
- if not getting:
- self.writable_needed = True
- if self.base.is_name or self.base.is_attribute:
- self.base.entry.type.writable_needed = True
- self.memslice_index = (not newaxes and len(indices) == self.base.type.ndim)
- axes = []
- index_type = PyrexTypes.c_py_ssize_t_type
- new_indices = []
- if len(indices) - len(newaxes) > self.base.type.ndim:
- self.type = error_type
- error(indices[self.base.type.ndim].pos,
- "Too many indices specified for type %s" % self.base.type)
- return self
- axis_idx = 0
- for i, index in enumerate(indices[:]):
- index = index.analyse_types(env)
- if index.is_none:
- self.is_memview_slice = True
- new_indices.append(index)
- axes.append(('direct', 'strided'))
- continue
- access, packing = self.base.type.axes[axis_idx]
- axis_idx += 1
- if index.is_slice:
- self.is_memview_slice = True
- if index.step.is_none:
- axes.append((access, packing))
- else:
- axes.append((access, 'strided'))
- # Coerce start, stop and step to temps of the right type
- for attr in ('start', 'stop', 'step'):
- value = getattr(index, attr)
- if not value.is_none:
- value = value.coerce_to(index_type, env)
- #value = value.coerce_to_temp(env)
- setattr(index, attr, value)
- new_indices.append(value)
- elif index.type.is_int or index.type.is_pyobject:
- if index.type.is_pyobject and not self.warned_untyped_idx:
- warning(index.pos, "Index should be typed for more efficient access", level=2)
- MemoryViewIndexNode.warned_untyped_idx = True
- self.is_memview_index = True
- index = index.coerce_to(index_type, env)
- indices[i] = index
- new_indices.append(index)
- else:
- self.type = error_type
- error(index.pos, "Invalid index for memoryview specified, type %s" % index.type)
- return self
- ### FIXME: replace by MemoryViewSliceNode if is_memview_slice ?
- self.is_memview_index = self.is_memview_index and not self.is_memview_slice
- self.indices = new_indices
- # All indices with all start/stop/step for slices.
- # We need to keep this around.
- self.original_indices = indices
- self.nogil = env.nogil
- self.analyse_operation(env, getting, axes)
- self.wrap_in_nonecheck_node(env)
- return self
- def analyse_operation(self, env, getting, axes):
- self.none_error_message = "Cannot index None memoryview slice"
- self.analyse_buffer_index(env, getting)
- def analyse_broadcast_operation(self, rhs):
- """
- Support broadcasting for slice assignment.
- E.g.
- m_2d[...] = m_1d # or,
- m_1d[...] = m_2d # if the leading dimension has extent 1
- """
- if self.type.is_memoryviewslice:
- lhs = self
- if lhs.is_memview_broadcast or rhs.is_memview_broadcast:
- lhs.is_memview_broadcast = True
- rhs.is_memview_broadcast = True
- def analyse_as_memview_scalar_assignment(self, rhs):
- lhs = self.analyse_assignment(rhs)
- if lhs:
- rhs.is_memview_copy_assignment = lhs.is_memview_copy_assignment
- return lhs
- return self
- class MemoryViewSliceNode(MemoryViewIndexNode):
- is_memview_slice = True
- # No-op slicing operation, this node will be replaced
- is_ellipsis_noop = False
- is_memview_scalar_assignment = False
- is_memview_index = False
- is_memview_broadcast = False
- def analyse_ellipsis_noop(self, env, getting):
- """Slicing operations needing no evaluation, i.e. m[...] or m[:, :]"""
- ### FIXME: replace directly
- self.is_ellipsis_noop = all(
- index.is_slice and index.start.is_none and index.stop.is_none and index.step.is_none
- for index in self.indices)
- if self.is_ellipsis_noop:
- self.type = self.base.type
- def analyse_operation(self, env, getting, axes):
- from . import MemoryView
- if not getting:
- self.is_memview_broadcast = True
- self.none_error_message = "Cannot assign to None memoryview slice"
- else:
- self.none_error_message = "Cannot slice None memoryview slice"
- self.analyse_ellipsis_noop(env, getting)
- if self.is_ellipsis_noop:
- return
- self.index = None
- self.is_temp = True
- self.use_managed_ref = True
- if not MemoryView.validate_axes(self.pos, axes):
- self.type = error_type
- return
- self.type = PyrexTypes.MemoryViewSliceType(self.base.type.dtype, axes)
- if not (self.base.is_simple() or self.base.result_in_temp()):
- self.base = self.base.coerce_to_temp(env)
- def analyse_assignment(self, rhs):
- if not rhs.type.is_memoryviewslice and (
- self.type.dtype.assignable_from(rhs.type) or
- rhs.type.is_pyobject):
- # scalar assignment
- return MemoryCopyScalar(self.pos, self)
- else:
- return MemoryCopySlice(self.pos, self)
- def merged_indices(self, indices):
- """Return a new list of indices/slices with 'indices' merged into the current ones
- according to slicing rules.
- Is used to implement "view[i][j]" => "view[i, j]".
- Return None if the indices cannot (easily) be merged at compile time.
- """
- if not indices:
- return None
- # NOTE: Need to evaluate "self.original_indices" here as they might differ from "self.indices".
- new_indices = self.original_indices[:]
- indices = indices[:]
- for i, s in enumerate(self.original_indices):
- if s.is_slice:
- if s.start.is_none and s.stop.is_none and s.step.is_none:
- # Full slice found, replace by index.
- new_indices[i] = indices[0]
- indices.pop(0)
- if not indices:
- return new_indices
- else:
- # Found something non-trivial, e.g. a partial slice.
- return None
- elif not s.type.is_int:
- # Not a slice, not an integer index => could be anything...
- return None
- if indices:
- if len(new_indices) + len(indices) > self.base.type.ndim:
- return None
- new_indices += indices
- return new_indices
- def is_simple(self):
- if self.is_ellipsis_noop:
- # TODO: fix SimpleCallNode.is_simple()
- return self.base.is_simple() or self.base.result_in_temp()
- return self.result_in_temp()
- def calculate_result_code(self):
- """This is called in case this is a no-op slicing node"""
- return self.base.result()
- def generate_result_code(self, code):
- if self.is_ellipsis_noop:
- return ### FIXME: remove
- buffer_entry = self.buffer_entry()
- have_gil = not self.in_nogil_context
- # TODO Mark: this is insane, do it better
- have_slices = False
- it = iter(self.indices)
- for index in self.original_indices:
- if index.is_slice:
- have_slices = True
- if not index.start.is_none:
- index.start = next(it)
- if not index.stop.is_none:
- index.stop = next(it)
- if not index.step.is_none:
- index.step = next(it)
- else:
- next(it)
- assert not list(it)
- buffer_entry.generate_buffer_slice_code(
- code, self.original_indices, self.result(),
- have_gil=have_gil, have_slices=have_slices,
- directives=code.globalstate.directives)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
- if self.is_ellipsis_noop:
- self.generate_subexpr_evaluation_code(code)
- else:
- self.generate_evaluation_code(code)
- if self.is_memview_scalar_assignment:
- self.generate_memoryviewslice_assign_scalar_code(rhs, code)
- else:
- self.generate_memoryviewslice_setslice_code(rhs, code)
- if self.is_ellipsis_noop:
- self.generate_subexpr_disposal_code(code)
- else:
- self.generate_disposal_code(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- class MemoryCopyNode(ExprNode):
- """
- Wraps a memoryview slice for slice assignment.
- dst: destination mememoryview slice
- """
- subexprs = ['dst']
- def __init__(self, pos, dst):
- super(MemoryCopyNode, self).__init__(pos)
- self.dst = dst
- self.type = dst.type
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
- self.dst.generate_evaluation_code(code)
- self._generate_assignment_code(rhs, code)
- self.dst.generate_disposal_code(code)
- self.dst.free_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- class MemoryCopySlice(MemoryCopyNode):
- """
- Copy the contents of slice src to slice dst. Does not support indirect
- slices.
- memslice1[...] = memslice2
- memslice1[:] = memslice2
- """
- is_memview_copy_assignment = True
- copy_slice_cname = "__pyx_memoryview_copy_contents"
- def _generate_assignment_code(self, src, code):
- dst = self.dst
- src.type.assert_direct_dims(src.pos)
- dst.type.assert_direct_dims(dst.pos)
- code.putln(code.error_goto_if_neg(
- "%s(%s, %s, %d, %d, %d)" % (self.copy_slice_cname,
- src.result(), dst.result(),
- src.type.ndim, dst.type.ndim,
- dst.type.dtype.is_pyobject),
- dst.pos))
- class MemoryCopyScalar(MemoryCopyNode):
- """
- Assign a scalar to a slice. dst must be simple, scalar will be assigned
- to a correct type and not just something assignable.
- memslice1[...] = 0.0
- memslice1[:] = 0.0
- """
- def __init__(self, pos, dst):
- super(MemoryCopyScalar, self).__init__(pos, dst)
- self.type = dst.type.dtype
- def _generate_assignment_code(self, scalar, code):
- from . import MemoryView
- self.dst.type.assert_direct_dims(self.dst.pos)
- dtype = self.dst.type.dtype
- type_decl = dtype.declaration_code("")
- slice_decl = self.dst.type.declaration_code("")
- code.begin_block()
- code.putln("%s __pyx_temp_scalar = %s;" % (type_decl, scalar.result()))
- if self.dst.result_in_temp() or self.dst.is_simple():
- dst_temp = self.dst.result()
- else:
- code.putln("%s __pyx_temp_slice = %s;" % (slice_decl, self.dst.result()))
- dst_temp = "__pyx_temp_slice"
- slice_iter_obj = MemoryView.slice_iter(self.dst.type, dst_temp,
- self.dst.type.ndim, code)
- p = slice_iter_obj.start_loops()
- if dtype.is_pyobject:
- code.putln("Py_DECREF(*(PyObject **) %s);" % p)
- code.putln("*((%s *) %s) = __pyx_temp_scalar;" % (type_decl, p))
- if dtype.is_pyobject:
- code.putln("Py_INCREF(__pyx_temp_scalar);")
- slice_iter_obj.end_loops()
- code.end_block()
- class SliceIndexNode(ExprNode):
- # 2-element slice indexing
- #
- # base ExprNode
- # start ExprNode or None
- # stop ExprNode or None
- # slice ExprNode or None constant slice object
- subexprs = ['base', 'start', 'stop', 'slice']
- slice = None
- def infer_type(self, env):
- base_type = self.base.infer_type(env)
- if base_type.is_string or base_type.is_cpp_class:
- return bytes_type
- elif base_type.is_pyunicode_ptr:
- return unicode_type
- elif base_type in (bytes_type, bytearray_type, str_type, unicode_type,
- basestring_type, list_type, tuple_type):
- return base_type
- elif base_type.is_ptr or base_type.is_array:
- return PyrexTypes.c_array_type(base_type.base_type, None)
- return py_object_type
- def inferable_item_node(self, index=0):
- # slicing shouldn't change the result type of the base, but the index might
- if index is not not_a_constant and self.start:
- if self.start.has_constant_result():
- index += self.start.constant_result
- else:
- index = not_a_constant
- return self.base.inferable_item_node(index)
- def may_be_none(self):
- base_type = self.base.type
- if base_type:
- if base_type.is_string:
- return False
- if base_type in (bytes_type, str_type, unicode_type,
- basestring_type, list_type, tuple_type):
- return False
- return ExprNode.may_be_none(self)
- def calculate_constant_result(self):
- if self.start is None:
- start = None
- else:
- start = self.start.constant_result
- if self.stop is None:
- stop = None
- else:
- stop = self.stop.constant_result
- self.constant_result = self.base.constant_result[start:stop]
- def compile_time_value(self, denv):
- base = self.base.compile_time_value(denv)
- if self.start is None:
- start = 0
- else:
- start = self.start.compile_time_value(denv)
- if self.stop is None:
- stop = None
- else:
- stop = self.stop.compile_time_value(denv)
- try:
- return base[start:stop]
- except Exception as e:
- self.compile_time_value_error(e)
- def analyse_target_declaration(self, env):
- pass
- def analyse_target_types(self, env):
- node = self.analyse_types(env, getting=False)
- # when assigning, we must accept any Python type
- if node.type.is_pyobject:
- node.type = py_object_type
- return node
- def analyse_types(self, env, getting=True):
- self.base = self.base.analyse_types(env)
- if self.base.type.is_buffer or self.base.type.is_pythran_expr or self.base.type.is_memoryviewslice:
- none_node = NoneNode(self.pos)
- index = SliceNode(self.pos,
- start=self.start or none_node,
- stop=self.stop or none_node,
- step=none_node)
- index_node = IndexNode(self.pos, index=index, base=self.base)
- return index_node.analyse_base_and_index_types(
- env, getting=getting, setting=not getting,
- analyse_base=False)
- if self.start:
- self.start = self.start.analyse_types(env)
- if self.stop:
- self.stop = self.stop.analyse_types(env)
- if not env.directives['wraparound']:
- check_negative_indices(self.start, self.stop)
- base_type = self.base.type
- if base_type.is_array and not getting:
- # cannot assign directly to C array => try to assign by making a copy
- if not self.start and not self.stop:
- self.type = base_type
- else:
- self.type = PyrexTypes.CPtrType(base_type.base_type)
- elif base_type.is_string or base_type.is_cpp_string:
- self.type = default_str_type(env)
- elif base_type.is_pyunicode_ptr:
- self.type = unicode_type
- elif base_type.is_ptr:
- self.type = base_type
- elif base_type.is_array:
- # we need a ptr type here instead of an array type, as
- # array types can result in invalid type casts in the C
- # code
- self.type = PyrexTypes.CPtrType(base_type.base_type)
- else:
- self.base = self.base.coerce_to_pyobject(env)
- self.type = py_object_type
- if base_type.is_builtin_type:
- # slicing builtin types returns something of the same type
- self.type = base_type
- self.base = self.base.as_none_safe_node("'NoneType' object is not subscriptable")
- if self.type is py_object_type:
- if (not self.start or self.start.is_literal) and \
- (not self.stop or self.stop.is_literal):
- # cache the constant slice object, in case we need it
- none_node = NoneNode(self.pos)
- self.slice = SliceNode(
- self.pos,
- start=copy.deepcopy(self.start or none_node),
- stop=copy.deepcopy(self.stop or none_node),
- step=none_node
- ).analyse_types(env)
- else:
- c_int = PyrexTypes.c_py_ssize_t_type
- def allow_none(node, default_value, env):
- # Coerce to Py_ssize_t, but allow None as meaning the default slice bound.
- from .UtilNodes import EvalWithTempExprNode, ResultRefNode
- node_ref = ResultRefNode(node)
- new_expr = CondExprNode(
- node.pos,
- true_val=IntNode(
- node.pos,
- type=c_int,
- value=default_value,
- constant_result=int(default_value) if default_value.isdigit() else not_a_constant,
- ),
- false_val=node_ref.coerce_to(c_int, env),
- test=PrimaryCmpNode(
- node.pos,
- operand1=node_ref,
- operator='is',
- operand2=NoneNode(node.pos),
- ).analyse_types(env)
- ).analyse_result_type(env)
- return EvalWithTempExprNode(node_ref, new_expr)
- if self.start:
- if self.start.type.is_pyobject:
- self.start = allow_none(self.start, '0', env)
- self.start = self.start.coerce_to(c_int, env)
- if self.stop:
- if self.stop.type.is_pyobject:
- self.stop = allow_none(self.stop, 'PY_SSIZE_T_MAX', env)
- self.stop = self.stop.coerce_to(c_int, env)
- self.is_temp = 1
- return self
- def analyse_as_type(self, env):
- base_type = self.base.analyse_as_type(env)
- if base_type and not base_type.is_pyobject:
- if not self.start and not self.stop:
- # memory view
- from . import MemoryView
- env.use_utility_code(MemoryView.view_utility_code)
- none_node = NoneNode(self.pos)
- slice_node = SliceNode(
- self.pos,
- start=none_node,
- stop=none_node,
- step=none_node,
- )
- return PyrexTypes.MemoryViewSliceType(
- base_type, MemoryView.get_axes_specs(env, [slice_node]))
- return None
- nogil_check = Node.gil_error
- gil_message = "Slicing Python object"
- get_slice_utility_code = TempitaUtilityCode.load(
- "SliceObject", "ObjectHandling.c", context={'access': 'Get'})
- set_slice_utility_code = TempitaUtilityCode.load(
- "SliceObject", "ObjectHandling.c", context={'access': 'Set'})
- def coerce_to(self, dst_type, env):
- if ((self.base.type.is_string or self.base.type.is_cpp_string)
- and dst_type in (bytes_type, bytearray_type, str_type, unicode_type)):
- if (dst_type not in (bytes_type, bytearray_type)
- and not env.directives['c_string_encoding']):
- error(self.pos,
- "default encoding required for conversion from '%s' to '%s'" %
- (self.base.type, dst_type))
- self.type = dst_type
- if dst_type.is_array and self.base.type.is_array:
- if not self.start and not self.stop:
- # redundant slice building, copy C arrays directly
- return self.base.coerce_to(dst_type, env)
- # else: check array size if possible
- return super(SliceIndexNode, self).coerce_to(dst_type, env)
- def generate_result_code(self, code):
- if not self.type.is_pyobject:
- error(self.pos,
- "Slicing is not currently supported for '%s'." % self.type)
- return
- base_result = self.base.result()
- result = self.result()
- start_code = self.start_code()
- stop_code = self.stop_code()
- if self.base.type.is_string:
- base_result = self.base.result()
- if self.base.type not in (PyrexTypes.c_char_ptr_type, PyrexTypes.c_const_char_ptr_type):
- base_result = '((const char*)%s)' % base_result
- if self.type is bytearray_type:
- type_name = 'ByteArray'
- else:
- type_name = self.type.name.title()
- if self.stop is None:
- code.putln(
- "%s = __Pyx_Py%s_FromString(%s + %s); %s" % (
- result,
- type_name,
- base_result,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- else:
- code.putln(
- "%s = __Pyx_Py%s_FromStringAndSize(%s + %s, %s - %s); %s" % (
- result,
- type_name,
- base_result,
- start_code,
- stop_code,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- elif self.base.type.is_pyunicode_ptr:
- base_result = self.base.result()
- if self.base.type != PyrexTypes.c_py_unicode_ptr_type:
- base_result = '((const Py_UNICODE*)%s)' % base_result
- if self.stop is None:
- code.putln(
- "%s = __Pyx_PyUnicode_FromUnicode(%s + %s); %s" % (
- result,
- base_result,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- else:
- code.putln(
- "%s = __Pyx_PyUnicode_FromUnicodeAndLength(%s + %s, %s - %s); %s" % (
- result,
- base_result,
- start_code,
- stop_code,
- start_code,
- code.error_goto_if_null(result, self.pos)))
- elif self.base.type is unicode_type:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyUnicode_Substring", "StringTools.c"))
- code.putln(
- "%s = __Pyx_PyUnicode_Substring(%s, %s, %s); %s" % (
- result,
- base_result,
- start_code,
- stop_code,
- code.error_goto_if_null(result, self.pos)))
- elif self.type is py_object_type:
- code.globalstate.use_utility_code(self.get_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
- code.putln(
- "%s = __Pyx_PyObject_GetSlice(%s, %s, %s, %s, %s, %s, %d, %d, %d); %s" % (
- result,
- self.base.py_result(),
- c_start, c_stop,
- py_start, py_stop, py_slice,
- has_c_start, has_c_stop,
- bool(code.globalstate.directives['wraparound']),
- code.error_goto_if_null(result, self.pos)))
- else:
- if self.base.type is list_type:
- code.globalstate.use_utility_code(
- TempitaUtilityCode.load_cached("SliceTupleAndList", "ObjectHandling.c"))
- cfunc = '__Pyx_PyList_GetSlice'
- elif self.base.type is tuple_type:
- code.globalstate.use_utility_code(
- TempitaUtilityCode.load_cached("SliceTupleAndList", "ObjectHandling.c"))
- cfunc = '__Pyx_PyTuple_GetSlice'
- else:
- cfunc = 'PySequence_GetSlice'
- code.putln(
- "%s = %s(%s, %s, %s); %s" % (
- result,
- cfunc,
- self.base.py_result(),
- start_code,
- stop_code,
- code.error_goto_if_null(result, self.pos)))
- code.put_gotref(self.py_result())
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- self.generate_subexpr_evaluation_code(code)
- if self.type.is_pyobject:
- code.globalstate.use_utility_code(self.set_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
- code.put_error_if_neg(self.pos,
- "__Pyx_PyObject_SetSlice(%s, %s, %s, %s, %s, %s, %s, %d, %d, %d)" % (
- self.base.py_result(),
- rhs.py_result(),
- c_start, c_stop,
- py_start, py_stop, py_slice,
- has_c_start, has_c_stop,
- bool(code.globalstate.directives['wraparound'])))
- else:
- start_offset = self.start_code() if self.start else '0'
- if rhs.type.is_array:
- array_length = rhs.type.size
- self.generate_slice_guard_code(code, array_length)
- else:
- array_length = '%s - %s' % (self.stop_code(), start_offset)
- code.globalstate.use_utility_code(UtilityCode.load_cached("IncludeStringH", "StringTools.c"))
- code.putln("memcpy(&(%s[%s]), %s, sizeof(%s[0]) * (%s));" % (
- self.base.result(), start_offset,
- rhs.result(),
- self.base.result(), array_length
- ))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- if not self.base.type.is_pyobject:
- error(self.pos,
- "Deleting slices is only supported for Python types, not '%s'." % self.type)
- return
- self.generate_subexpr_evaluation_code(code)
- code.globalstate.use_utility_code(self.set_slice_utility_code)
- (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice) = self.get_slice_config()
- code.put_error_if_neg(self.pos,
- "__Pyx_PyObject_DelSlice(%s, %s, %s, %s, %s, %s, %d, %d, %d)" % (
- self.base.py_result(),
- c_start, c_stop,
- py_start, py_stop, py_slice,
- has_c_start, has_c_stop,
- bool(code.globalstate.directives['wraparound'])))
- self.generate_subexpr_disposal_code(code)
- self.free_subexpr_temps(code)
- def get_slice_config(self):
- has_c_start, c_start, py_start = False, '0', 'NULL'
- if self.start:
- has_c_start = not self.start.type.is_pyobject
- if has_c_start:
- c_start = self.start.result()
- else:
- py_start = '&%s' % self.start.py_result()
- has_c_stop, c_stop, py_stop = False, '0', 'NULL'
- if self.stop:
- has_c_stop = not self.stop.type.is_pyobject
- if has_c_stop:
- c_stop = self.stop.result()
- else:
- py_stop = '&%s' % self.stop.py_result()
- py_slice = self.slice and '&%s' % self.slice.py_result() or 'NULL'
- return (has_c_start, has_c_stop, c_start, c_stop,
- py_start, py_stop, py_slice)
- def generate_slice_guard_code(self, code, target_size):
- if not self.base.type.is_array:
- return
- slice_size = self.base.type.size
- try:
- total_length = slice_size = int(slice_size)
- except ValueError:
- total_length = None
- start = stop = None
- if self.stop:
- stop = self.stop.result()
- try:
- stop = int(stop)
- if stop < 0:
- if total_length is None:
- slice_size = '%s + %d' % (slice_size, stop)
- else:
- slice_size += stop
- else:
- slice_size = stop
- stop = None
- except ValueError:
- pass
- if self.start:
- start = self.start.result()
- try:
- start = int(start)
- if start < 0:
- if total_length is None:
- start = '%s + %d' % (self.base.type.size, start)
- else:
- start += total_length
- if isinstance(slice_size, _py_int_types):
- slice_size -= start
- else:
- slice_size = '%s - (%s)' % (slice_size, start)
- start = None
- except ValueError:
- pass
- runtime_check = None
- compile_time_check = False
- try:
- int_target_size = int(target_size)
- except ValueError:
- int_target_size = None
- else:
- compile_time_check = isinstance(slice_size, _py_int_types)
- if compile_time_check and slice_size < 0:
- if int_target_size > 0:
- error(self.pos, "Assignment to empty slice.")
- elif compile_time_check and start is None and stop is None:
- # we know the exact slice length
- if int_target_size != slice_size:
- error(self.pos, "Assignment to slice of wrong length, expected %s, got %s" % (
- slice_size, target_size))
- elif start is not None:
- if stop is None:
- stop = slice_size
- runtime_check = "(%s)-(%s)" % (stop, start)
- elif stop is not None:
- runtime_check = stop
- else:
- runtime_check = slice_size
- if runtime_check:
- code.putln("if (unlikely((%s) != (%s))) {" % (runtime_check, target_size))
- code.putln(
- 'PyErr_Format(PyExc_ValueError, "Assignment to slice of wrong length,'
- ' expected %%" CYTHON_FORMAT_SSIZE_T "d, got %%" CYTHON_FORMAT_SSIZE_T "d",'
- ' (Py_ssize_t)(%s), (Py_ssize_t)(%s));' % (
- target_size, runtime_check))
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- def start_code(self):
- if self.start:
- return self.start.result()
- else:
- return "0"
- def stop_code(self):
- if self.stop:
- return self.stop.result()
- elif self.base.type.is_array:
- return self.base.type.size
- else:
- return "PY_SSIZE_T_MAX"
- def calculate_result_code(self):
- # self.result() is not used, but this method must exist
- return "<unused>"
- class SliceNode(ExprNode):
- # start:stop:step in subscript list
- #
- # start ExprNode
- # stop ExprNode
- # step ExprNode
- subexprs = ['start', 'stop', 'step']
- is_slice = True
- type = slice_type
- is_temp = 1
- def calculate_constant_result(self):
- self.constant_result = slice(
- self.start.constant_result,
- self.stop.constant_result,
- self.step.constant_result)
- def compile_time_value(self, denv):
- start = self.start.compile_time_value(denv)
- stop = self.stop.compile_time_value(denv)
- step = self.step.compile_time_value(denv)
- try:
- return slice(start, stop, step)
- except Exception as e:
- self.compile_time_value_error(e)
- def may_be_none(self):
- return False
- def analyse_types(self, env):
- start = self.start.analyse_types(env)
- stop = self.stop.analyse_types(env)
- step = self.step.analyse_types(env)
- self.start = start.coerce_to_pyobject(env)
- self.stop = stop.coerce_to_pyobject(env)
- self.step = step.coerce_to_pyobject(env)
- if self.start.is_literal and self.stop.is_literal and self.step.is_literal:
- self.is_literal = True
- self.is_temp = False
- return self
- gil_message = "Constructing Python slice object"
- def calculate_result_code(self):
- return self.result_code
- def generate_result_code(self, code):
- if self.is_literal:
- dedup_key = make_dedup_key(self.type, (self,))
- self.result_code = code.get_py_const(py_object_type, 'slice', cleanup_level=2, dedup_key=dedup_key)
- code = code.get_cached_constants_writer(self.result_code)
- if code is None:
- return # already initialised
- code.mark_pos(self.pos)
- code.putln(
- "%s = PySlice_New(%s, %s, %s); %s" % (
- self.result(),
- self.start.py_result(),
- self.stop.py_result(),
- self.step.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- if self.is_literal:
- code.put_giveref(self.py_result())
- class SliceIntNode(SliceNode):
- # start:stop:step in subscript list
- # This is just a node to hold start,stop and step nodes that can be
- # converted to integers. This does not generate a slice python object.
- #
- # start ExprNode
- # stop ExprNode
- # step ExprNode
- is_temp = 0
- def calculate_constant_result(self):
- self.constant_result = slice(
- self.start.constant_result,
- self.stop.constant_result,
- self.step.constant_result)
- def compile_time_value(self, denv):
- start = self.start.compile_time_value(denv)
- stop = self.stop.compile_time_value(denv)
- step = self.step.compile_time_value(denv)
- try:
- return slice(start, stop, step)
- except Exception as e:
- self.compile_time_value_error(e)
- def may_be_none(self):
- return False
- def analyse_types(self, env):
- self.start = self.start.analyse_types(env)
- self.stop = self.stop.analyse_types(env)
- self.step = self.step.analyse_types(env)
- if not self.start.is_none:
- self.start = self.start.coerce_to_integer(env)
- if not self.stop.is_none:
- self.stop = self.stop.coerce_to_integer(env)
- if not self.step.is_none:
- self.step = self.step.coerce_to_integer(env)
- if self.start.is_literal and self.stop.is_literal and self.step.is_literal:
- self.is_literal = True
- self.is_temp = False
- return self
- def calculate_result_code(self):
- pass
- def generate_result_code(self, code):
- for a in self.start,self.stop,self.step:
- if isinstance(a, CloneNode):
- a.arg.result()
- class CallNode(ExprNode):
- # allow overriding the default 'may_be_none' behaviour
- may_return_none = None
- def infer_type(self, env):
- # TODO(robertwb): Reduce redundancy with analyse_types.
- function = self.function
- func_type = function.infer_type(env)
- if isinstance(function, NewExprNode):
- # note: needs call to infer_type() above
- return PyrexTypes.CPtrType(function.class_type)
- if func_type is py_object_type:
- # function might have lied for safety => try to find better type
- entry = getattr(function, 'entry', None)
- if entry is not None:
- func_type = entry.type or func_type
- if func_type.is_ptr:
- func_type = func_type.base_type
- if func_type.is_cfunction:
- if getattr(self.function, 'entry', None) and hasattr(self, 'args'):
- alternatives = self.function.entry.all_alternatives()
- arg_types = [arg.infer_type(env) for arg in self.args]
- func_entry = PyrexTypes.best_match(arg_types, alternatives)
- if func_entry:
- func_type = func_entry.type
- if func_type.is_ptr:
- func_type = func_type.base_type
- return func_type.return_type
- return func_type.return_type
- elif func_type is type_type:
- if function.is_name and function.entry and function.entry.type:
- result_type = function.entry.type
- if result_type.is_extension_type:
- return result_type
- elif result_type.is_builtin_type:
- if function.entry.name == 'float':
- return PyrexTypes.c_double_type
- elif function.entry.name in Builtin.types_that_construct_their_instance:
- return result_type
- return py_object_type
- def type_dependencies(self, env):
- # TODO: Update when Danilo's C++ code merged in to handle the
- # the case of function overloading.
- return self.function.type_dependencies(env)
- def is_simple(self):
- # C function calls could be considered simple, but they may
- # have side-effects that may hit when multiple operations must
- # be effected in order, e.g. when constructing the argument
- # sequence for a function call or comparing values.
- return False
- def may_be_none(self):
- if self.may_return_none is not None:
- return self.may_return_none
- func_type = self.function.type
- if func_type is type_type and self.function.is_name:
- entry = self.function.entry
- if entry.type.is_extension_type:
- return False
- if (entry.type.is_builtin_type and
- entry.name in Builtin.types_that_construct_their_instance):
- return False
- return ExprNode.may_be_none(self)
- def set_py_result_type(self, function, func_type=None):
- if func_type is None:
- func_type = function.type
- if func_type is Builtin.type_type and (
- function.is_name and
- function.entry and
- function.entry.is_builtin and
- function.entry.name in Builtin.types_that_construct_their_instance):
- # calling a builtin type that returns a specific object type
- if function.entry.name == 'float':
- # the following will come true later on in a transform
- self.type = PyrexTypes.c_double_type
- self.result_ctype = PyrexTypes.c_double_type
- else:
- self.type = Builtin.builtin_types[function.entry.name]
- self.result_ctype = py_object_type
- self.may_return_none = False
- elif function.is_name and function.type_entry:
- # We are calling an extension type constructor. As long as we do not
- # support __new__(), the result type is clear
- self.type = function.type_entry.type
- self.result_ctype = py_object_type
- self.may_return_none = False
- else:
- self.type = py_object_type
- def analyse_as_type_constructor(self, env):
- type = self.function.analyse_as_type(env)
- if type and type.is_struct_or_union:
- args, kwds = self.explicit_args_kwds()
- items = []
- for arg, member in zip(args, type.scope.var_entries):
- items.append(DictItemNode(pos=arg.pos, key=StringNode(pos=arg.pos, value=member.name), value=arg))
- if kwds:
- items += kwds.key_value_pairs
- self.key_value_pairs = items
- self.__class__ = DictNode
- self.analyse_types(env) # FIXME
- self.coerce_to(type, env)
- return True
- elif type and type.is_cpp_class:
- self.args = [ arg.analyse_types(env) for arg in self.args ]
- constructor = type.scope.lookup("<init>")
- if not constructor:
- error(self.function.pos, "no constructor found for C++ type '%s'" % self.function.name)
- self.type = error_type
- return self
- self.function = RawCNameExprNode(self.function.pos, constructor.type)
- self.function.entry = constructor
- self.function.set_cname(type.empty_declaration_code())
- self.analyse_c_function_call(env)
- self.type = type
- return True
- def is_lvalue(self):
- return self.type.is_reference
- def nogil_check(self, env):
- func_type = self.function_type()
- if func_type.is_pyobject:
- self.gil_error()
- elif not func_type.is_error and not getattr(func_type, 'nogil', False):
- self.gil_error()
- gil_message = "Calling gil-requiring function"
- class SimpleCallNode(CallNode):
- # Function call without keyword, * or ** args.
- #
- # function ExprNode
- # args [ExprNode]
- # arg_tuple ExprNode or None used internally
- # self ExprNode or None used internally
- # coerced_self ExprNode or None used internally
- # wrapper_call bool used internally
- # has_optional_args bool used internally
- # nogil bool used internally
- subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple']
- self = None
- coerced_self = None
- arg_tuple = None
- wrapper_call = False
- has_optional_args = False
- nogil = False
- analysed = False
- overflowcheck = False
- def compile_time_value(self, denv):
- function = self.function.compile_time_value(denv)
- args = [arg.compile_time_value(denv) for arg in self.args]
- try:
- return function(*args)
- except Exception as e:
- self.compile_time_value_error(e)
- def analyse_as_type(self, env):
- attr = self.function.as_cython_attribute()
- if attr == 'pointer':
- if len(self.args) != 1:
- error(self.args.pos, "only one type allowed.")
- else:
- type = self.args[0].analyse_as_type(env)
- if not type:
- error(self.args[0].pos, "Unknown type")
- else:
- return PyrexTypes.CPtrType(type)
- elif attr == 'typeof':
- if len(self.args) != 1:
- error(self.args.pos, "only one type allowed.")
- operand = self.args[0].analyse_types(env)
- return operand.type
- def explicit_args_kwds(self):
- return self.args, None
- def analyse_types(self, env):
- if self.analyse_as_type_constructor(env):
- return self
- if self.analysed:
- return self
- self.analysed = True
- self.function.is_called = 1
- self.function = self.function.analyse_types(env)
- function = self.function
- if function.is_attribute and function.entry and function.entry.is_cmethod:
- # Take ownership of the object from which the attribute
- # was obtained, because we need to pass it as 'self'.
- self.self = function.obj
- function.obj = CloneNode(self.self)
- func_type = self.function_type()
- self.is_numpy_call_with_exprs = False
- if (has_np_pythran(env) and function.is_numpy_attribute and
- pythran_is_numpy_func_supported(function)):
- has_pythran_args = True
- self.arg_tuple = TupleNode(self.pos, args = self.args)
- self.arg_tuple = self.arg_tuple.analyse_types(env)
- for arg in self.arg_tuple.args:
- has_pythran_args &= is_pythran_supported_node_or_none(arg)
- self.is_numpy_call_with_exprs = bool(has_pythran_args)
- if self.is_numpy_call_with_exprs:
- env.add_include_file(pythran_get_func_include_file(function))
- return NumPyMethodCallNode.from_node(
- self,
- function_cname=pythran_functor(function),
- arg_tuple=self.arg_tuple,
- type=PythranExpr(pythran_func_type(function, self.arg_tuple.args)),
- )
- elif func_type.is_pyobject:
- self.arg_tuple = TupleNode(self.pos, args = self.args)
- self.arg_tuple = self.arg_tuple.analyse_types(env).coerce_to_pyobject(env)
- self.args = None
- self.set_py_result_type(function, func_type)
- self.is_temp = 1
- else:
- self.args = [ arg.analyse_types(env) for arg in self.args ]
- self.analyse_c_function_call(env)
- if func_type.exception_check == '+':
- self.is_temp = True
- return self
- def function_type(self):
- # Return the type of the function being called, coercing a function
- # pointer to a function if necessary. If the function has fused
- # arguments, return the specific type.
- func_type = self.function.type
- if func_type.is_ptr:
- func_type = func_type.base_type
- return func_type
- def analyse_c_function_call(self, env):
- func_type = self.function.type
- if func_type is error_type:
- self.type = error_type
- return
- if func_type.is_cfunction and func_type.is_static_method:
- if self.self and self.self.type.is_extension_type:
- # To support this we'd need to pass self to determine whether
- # it was overloaded in Python space (possibly via a Cython
- # superclass turning a cdef method into a cpdef one).
- error(self.pos, "Cannot call a static method on an instance variable.")
- args = self.args
- elif self.self:
- args = [self.self] + self.args
- else:
- args = self.args
- if func_type.is_cpp_class:
- overloaded_entry = self.function.type.scope.lookup("operator()")
- if overloaded_entry is None:
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- elif hasattr(self.function, 'entry'):
- overloaded_entry = self.function.entry
- elif self.function.is_subscript and self.function.is_fused_index:
- overloaded_entry = self.function.type.entry
- else:
- overloaded_entry = None
- if overloaded_entry:
- if self.function.type.is_fused:
- functypes = self.function.type.get_all_specialized_function_types()
- alternatives = [f.entry for f in functypes]
- else:
- alternatives = overloaded_entry.all_alternatives()
- entry = PyrexTypes.best_match(
- [arg.type for arg in args], alternatives, self.pos, env, args)
- if not entry:
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- entry.used = True
- if not func_type.is_cpp_class:
- self.function.entry = entry
- self.function.type = entry.type
- func_type = self.function_type()
- else:
- entry = None
- func_type = self.function_type()
- if not func_type.is_cfunction:
- error(self.pos, "Calling non-function type '%s'" % func_type)
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- # Check no. of args
- max_nargs = len(func_type.args)
- expected_nargs = max_nargs - func_type.optional_arg_count
- actual_nargs = len(args)
- if func_type.optional_arg_count and expected_nargs != actual_nargs:
- self.has_optional_args = 1
- self.is_temp = 1
- # check 'self' argument
- if entry and entry.is_cmethod and func_type.args and not func_type.is_static_method:
- formal_arg = func_type.args[0]
- arg = args[0]
- if formal_arg.not_none:
- if self.self:
- self.self = self.self.as_none_safe_node(
- "'NoneType' object has no attribute '%{0}s'".format('.30' if len(entry.name) <= 30 else ''),
- error='PyExc_AttributeError',
- format_args=[entry.name])
- else:
- # unbound method
- arg = arg.as_none_safe_node(
- "descriptor '%s' requires a '%s' object but received a 'NoneType'",
- format_args=[entry.name, formal_arg.type.name])
- if self.self:
- if formal_arg.accept_builtin_subtypes:
- arg = CMethodSelfCloneNode(self.self)
- else:
- arg = CloneNode(self.self)
- arg = self.coerced_self = arg.coerce_to(formal_arg.type, env)
- elif formal_arg.type.is_builtin_type:
- # special case: unbound methods of builtins accept subtypes
- arg = arg.coerce_to(formal_arg.type, env)
- if arg.type.is_builtin_type and isinstance(arg, PyTypeTestNode):
- arg.exact_builtin_type = False
- args[0] = arg
- # Coerce arguments
- some_args_in_temps = False
- for i in range(min(max_nargs, actual_nargs)):
- formal_arg = func_type.args[i]
- formal_type = formal_arg.type
- arg = args[i].coerce_to(formal_type, env)
- if formal_arg.not_none:
- # C methods must do the None checks at *call* time
- arg = arg.as_none_safe_node(
- "cannot pass None into a C function argument that is declared 'not None'")
- if arg.is_temp:
- if i > 0:
- # first argument in temp doesn't impact subsequent arguments
- some_args_in_temps = True
- elif arg.type.is_pyobject and not env.nogil:
- if i == 0 and self.self is not None:
- # a method's cloned "self" argument is ok
- pass
- elif arg.nonlocally_immutable():
- # plain local variables are ok
- pass
- else:
- # we do not safely own the argument's reference,
- # but we must make sure it cannot be collected
- # before we return from the function, so we create
- # an owned temp reference to it
- if i > 0: # first argument doesn't matter
- some_args_in_temps = True
- arg = arg.coerce_to_temp(env)
- args[i] = arg
- # handle additional varargs parameters
- for i in range(max_nargs, actual_nargs):
- arg = args[i]
- if arg.type.is_pyobject:
- if arg.type is str_type:
- arg_ctype = PyrexTypes.c_char_ptr_type
- else:
- arg_ctype = arg.type.default_coerced_ctype()
- if arg_ctype is None:
- error(self.args[i].pos,
- "Python object cannot be passed as a varargs parameter")
- else:
- args[i] = arg = arg.coerce_to(arg_ctype, env)
- if arg.is_temp and i > 0:
- some_args_in_temps = True
- if some_args_in_temps:
- # if some args are temps and others are not, they may get
- # constructed in the wrong order (temps first) => make
- # sure they are either all temps or all not temps (except
- # for the last argument, which is evaluated last in any
- # case)
- for i in range(actual_nargs-1):
- if i == 0 and self.self is not None:
- continue # self is ok
- arg = args[i]
- if arg.nonlocally_immutable():
- # locals, C functions, unassignable types are safe.
- pass
- elif arg.type.is_cpp_class:
- # Assignment has side effects, avoid.
- pass
- elif env.nogil and arg.type.is_pyobject:
- # can't copy a Python reference into a temp in nogil
- # env (this is safe: a construction would fail in
- # nogil anyway)
- pass
- else:
- #self.args[i] = arg.coerce_to_temp(env)
- # instead: issue a warning
- if i > 0 or i == 1 and self.self is not None: # skip first arg
- warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
- break
- self.args[:] = args
- # Calc result type and code fragment
- if isinstance(self.function, NewExprNode):
- self.type = PyrexTypes.CPtrType(self.function.class_type)
- else:
- self.type = func_type.return_type
- if self.function.is_name or self.function.is_attribute:
- func_entry = self.function.entry
- if func_entry and (func_entry.utility_code or func_entry.utility_code_definition):
- self.is_temp = 1 # currently doesn't work for self.calculate_result_code()
- if self.type.is_pyobject:
- self.result_ctype = py_object_type
- self.is_temp = 1
- elif func_type.exception_value is not None or func_type.exception_check:
- self.is_temp = 1
- elif self.type.is_memoryviewslice:
- self.is_temp = 1
- # func_type.exception_check = True
- if self.is_temp and self.type.is_reference:
- self.type = PyrexTypes.CFakeReferenceType(self.type.ref_base_type)
- # Called in 'nogil' context?
- self.nogil = env.nogil
- if (self.nogil and
- func_type.exception_check and
- func_type.exception_check != '+'):
- env.use_utility_code(pyerr_occurred_withgil_utility_code)
- # C++ exception handler
- if func_type.exception_check == '+':
- if func_type.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- self.overflowcheck = env.directives['overflowcheck']
- def calculate_result_code(self):
- return self.c_call_code()
- def c_call_code(self):
- func_type = self.function_type()
- if self.type is PyrexTypes.error_type or not func_type.is_cfunction:
- return "<error>"
- formal_args = func_type.args
- arg_list_code = []
- args = list(zip(formal_args, self.args))
- max_nargs = len(func_type.args)
- expected_nargs = max_nargs - func_type.optional_arg_count
- actual_nargs = len(self.args)
- for formal_arg, actual_arg in args[:expected_nargs]:
- arg_code = actual_arg.result_as(formal_arg.type)
- arg_list_code.append(arg_code)
- if func_type.is_overridable:
- arg_list_code.append(str(int(self.wrapper_call or self.function.entry.is_unbound_cmethod)))
- if func_type.optional_arg_count:
- if expected_nargs == actual_nargs:
- optional_args = 'NULL'
- else:
- optional_args = "&%s" % self.opt_arg_struct
- arg_list_code.append(optional_args)
- for actual_arg in self.args[len(formal_args):]:
- arg_list_code.append(actual_arg.result())
- result = "%s(%s)" % (self.function.result(), ', '.join(arg_list_code))
- return result
- def is_c_result_required(self):
- func_type = self.function_type()
- if not func_type.exception_value or func_type.exception_check == '+':
- return False # skip allocation of unused result temp
- return True
- def generate_evaluation_code(self, code):
- function = self.function
- if function.is_name or function.is_attribute:
- code.globalstate.use_entry_utility_code(function.entry)
- abs_function_cnames = ('abs', 'labs', '__Pyx_abs_longlong')
- is_signed_int = self.type.is_int and self.type.signed
- if self.overflowcheck and is_signed_int and function.result() in abs_function_cnames:
- code.globalstate.use_utility_code(UtilityCode.load_cached("Common", "Overflow.c"))
- code.putln('if (unlikely(%s == __PYX_MIN(%s))) {\
- PyErr_SetString(PyExc_OverflowError,\
- "Trying to take the absolute value of the most negative integer is not defined."); %s; }' % (
- self.args[0].result(),
- self.args[0].type.empty_declaration_code(),
- code.error_goto(self.pos)))
- if not function.type.is_pyobject or len(self.arg_tuple.args) > 1 or (
- self.arg_tuple.args and self.arg_tuple.is_literal):
- super(SimpleCallNode, self).generate_evaluation_code(code)
- return
- # Special case 0-args and try to avoid explicit tuple creation for Python calls with 1 arg.
- arg = self.arg_tuple.args[0] if self.arg_tuple.args else None
- subexprs = (self.self, self.coerced_self, function, arg)
- for subexpr in subexprs:
- if subexpr is not None:
- subexpr.generate_evaluation_code(code)
- code.mark_pos(self.pos)
- assert self.is_temp
- self.allocate_temp_result(code)
- if arg is None:
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCallNoArg", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_CallNoArg(%s); %s" % (
- self.result(),
- function.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- else:
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCallOneArg", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_CallOneArg(%s, %s); %s" % (
- self.result(),
- function.py_result(),
- arg.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- for subexpr in subexprs:
- if subexpr is not None:
- subexpr.generate_disposal_code(code)
- subexpr.free_temps(code)
- def generate_result_code(self, code):
- func_type = self.function_type()
- if func_type.is_pyobject:
- arg_code = self.arg_tuple.py_result()
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
- self.result(),
- self.function.py_result(),
- arg_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif func_type.is_cfunction:
- if self.has_optional_args:
- actual_nargs = len(self.args)
- expected_nargs = len(func_type.args) - func_type.optional_arg_count
- self.opt_arg_struct = code.funcstate.allocate_temp(
- func_type.op_arg_struct.base_type, manage_ref=True)
- code.putln("%s.%s = %s;" % (
- self.opt_arg_struct,
- Naming.pyrex_prefix + "n",
- len(self.args) - expected_nargs))
- args = list(zip(func_type.args, self.args))
- for formal_arg, actual_arg in args[expected_nargs:actual_nargs]:
- code.putln("%s.%s = %s;" % (
- self.opt_arg_struct,
- func_type.opt_arg_cname(formal_arg.name),
- actual_arg.result_as(formal_arg.type)))
- exc_checks = []
- if self.type.is_pyobject and self.is_temp:
- exc_checks.append("!%s" % self.result())
- elif self.type.is_memoryviewslice:
- assert self.is_temp
- exc_checks.append(self.type.error_condition(self.result()))
- elif func_type.exception_check != '+':
- exc_val = func_type.exception_value
- exc_check = func_type.exception_check
- if exc_val is not None:
- exc_checks.append("%s == %s" % (self.result(), func_type.return_type.cast_code(exc_val)))
- if exc_check:
- if self.nogil:
- exc_checks.append("__Pyx_ErrOccurredWithGIL()")
- else:
- exc_checks.append("PyErr_Occurred()")
- if self.is_temp or exc_checks:
- rhs = self.c_call_code()
- if self.result():
- lhs = "%s = " % self.result()
- if self.is_temp and self.type.is_pyobject:
- #return_type = self.type # func_type.return_type
- #print "SimpleCallNode.generate_result_code: casting", rhs, \
- # "from", return_type, "to pyobject" ###
- rhs = typecast(py_object_type, self.type, rhs)
- else:
- lhs = ""
- if func_type.exception_check == '+':
- translate_cpp_exception(code, self.pos, '%s%s;' % (lhs, rhs),
- self.result() if self.type.is_pyobject else None,
- func_type.exception_value, self.nogil)
- else:
- if exc_checks:
- goto_error = code.error_goto_if(" && ".join(exc_checks), self.pos)
- else:
- goto_error = ""
- code.putln("%s%s; %s" % (lhs, rhs, goto_error))
- if self.type.is_pyobject and self.result():
- code.put_gotref(self.py_result())
- if self.has_optional_args:
- code.funcstate.release_temp(self.opt_arg_struct)
- class NumPyMethodCallNode(ExprNode):
- # Pythran call to a NumPy function or method.
- #
- # function_cname string the function/method to call
- # arg_tuple TupleNode the arguments as an args tuple
- subexprs = ['arg_tuple']
- is_temp = True
- may_return_none = True
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- assert self.arg_tuple.mult_factor is None
- args = self.arg_tuple.args
- for arg in args:
- arg.generate_evaluation_code(code)
- code.putln("// function evaluation code for numpy function")
- code.putln("__Pyx_call_destructor(%s);" % self.result())
- code.putln("new (&%s) decltype(%s){%s{}(%s)};" % (
- self.result(),
- self.result(),
- self.function_cname,
- ", ".join(a.pythran_result() for a in args)))
- class PyMethodCallNode(SimpleCallNode):
- # Specialised call to a (potential) PyMethodObject with non-constant argument tuple.
- # Allows the self argument to be injected directly instead of repacking a tuple for it.
- #
- # function ExprNode the function/method object to call
- # arg_tuple TupleNode the arguments for the args tuple
- subexprs = ['function', 'arg_tuple']
- is_temp = True
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- self.function.generate_evaluation_code(code)
- assert self.arg_tuple.mult_factor is None
- args = self.arg_tuple.args
- for arg in args:
- arg.generate_evaluation_code(code)
- # make sure function is in temp so that we can replace the reference below if it's a method
- reuse_function_temp = self.function.is_temp
- if reuse_function_temp:
- function = self.function.result()
- else:
- function = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- self.function.make_owned_reference(code)
- code.put("%s = %s; " % (function, self.function.py_result()))
- self.function.generate_disposal_code(code)
- self.function.free_temps(code)
- self_arg = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln("%s = NULL;" % self_arg)
- arg_offset_cname = None
- if len(args) > 1:
- arg_offset_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
- code.putln("%s = 0;" % arg_offset_cname)
- def attribute_is_likely_method(attr):
- obj = attr.obj
- if obj.is_name and obj.entry.is_pyglobal:
- return False # more likely to be a function
- return True
- if self.function.is_attribute:
- likely_method = 'likely' if attribute_is_likely_method(self.function) else 'unlikely'
- elif self.function.is_name and self.function.cf_state:
- # not an attribute itself, but might have been assigned from one (e.g. bound method)
- for assignment in self.function.cf_state:
- value = assignment.rhs
- if value and value.is_attribute and value.obj.type and value.obj.type.is_pyobject:
- if attribute_is_likely_method(value):
- likely_method = 'likely'
- break
- else:
- likely_method = 'unlikely'
- else:
- likely_method = 'unlikely'
- code.putln("if (CYTHON_UNPACK_METHODS && %s(PyMethod_Check(%s))) {" % (likely_method, function))
- code.putln("%s = PyMethod_GET_SELF(%s);" % (self_arg, function))
- # the following is always true in Py3 (kept only for safety),
- # but is false for unbound methods in Py2
- code.putln("if (likely(%s)) {" % self_arg)
- code.putln("PyObject* function = PyMethod_GET_FUNCTION(%s);" % function)
- code.put_incref(self_arg, py_object_type)
- code.put_incref("function", py_object_type)
- # free method object as early to possible to enable reuse from CPython's freelist
- code.put_decref_set(function, "function")
- if len(args) > 1:
- code.putln("%s = 1;" % arg_offset_cname)
- code.putln("}")
- code.putln("}")
- if not args:
- # fastest special case: try to avoid tuple creation
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallNoArg", "ObjectHandling.c"))
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallOneArg", "ObjectHandling.c"))
- code.putln(
- "%s = (%s) ? __Pyx_PyObject_CallOneArg(%s, %s) : __Pyx_PyObject_CallNoArg(%s);" % (
- self.result(), self_arg,
- function, self_arg,
- function))
- code.put_xdecref_clear(self_arg, py_object_type)
- code.funcstate.release_temp(self_arg)
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- elif len(args) == 1:
- # fastest special case: try to avoid tuple creation
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCall2Args", "ObjectHandling.c"))
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCallOneArg", "ObjectHandling.c"))
- arg = args[0]
- code.putln(
- "%s = (%s) ? __Pyx_PyObject_Call2Args(%s, %s, %s) : __Pyx_PyObject_CallOneArg(%s, %s);" % (
- self.result(), self_arg,
- function, self_arg, arg.py_result(),
- function, arg.py_result()))
- code.put_xdecref_clear(self_arg, py_object_type)
- code.funcstate.release_temp(self_arg)
- arg.generate_disposal_code(code)
- arg.free_temps(code)
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyFunctionFastCall", "ObjectHandling.c"))
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyCFunctionFastCall", "ObjectHandling.c"))
- for test_func, call_prefix in [('PyFunction_Check', 'Py'), ('__Pyx_PyFastCFunction_Check', 'PyC')]:
- code.putln("#if CYTHON_FAST_%sCALL" % call_prefix.upper())
- code.putln("if (%s(%s)) {" % (test_func, function))
- code.putln("PyObject *%s[%d] = {%s, %s};" % (
- Naming.quick_temp_cname,
- len(args)+1,
- self_arg,
- ', '.join(arg.py_result() for arg in args)))
- code.putln("%s = __Pyx_%sFunction_FastCall(%s, %s+1-%s, %d+%s); %s" % (
- self.result(),
- call_prefix,
- function,
- Naming.quick_temp_cname,
- arg_offset_cname,
- len(args),
- arg_offset_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_xdecref_clear(self_arg, py_object_type)
- code.put_gotref(self.py_result())
- for arg in args:
- arg.generate_disposal_code(code)
- code.putln("} else")
- code.putln("#endif")
- code.putln("{")
- args_tuple = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln("%s = PyTuple_New(%d+%s); %s" % (
- args_tuple, len(args), arg_offset_cname,
- code.error_goto_if_null(args_tuple, self.pos)))
- code.put_gotref(args_tuple)
- if len(args) > 1:
- code.putln("if (%s) {" % self_arg)
- code.putln("__Pyx_GIVEREF(%s); PyTuple_SET_ITEM(%s, 0, %s); %s = NULL;" % (
- self_arg, args_tuple, self_arg, self_arg)) # stealing owned ref in this case
- code.funcstate.release_temp(self_arg)
- if len(args) > 1:
- code.putln("}")
- for i, arg in enumerate(args):
- arg.make_owned_reference(code)
- code.put_giveref(arg.py_result())
- code.putln("PyTuple_SET_ITEM(%s, %d+%s, %s);" % (
- args_tuple, i, arg_offset_cname, arg.py_result()))
- if len(args) > 1:
- code.funcstate.release_temp(arg_offset_cname)
- for arg in args:
- arg.generate_post_assignment_code(code)
- arg.free_temps(code)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
- self.result(),
- function, args_tuple,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- code.put_decref_clear(args_tuple, py_object_type)
- code.funcstate.release_temp(args_tuple)
- if len(args) == 1:
- code.putln("}")
- code.putln("}") # !CYTHON_FAST_PYCALL
- if reuse_function_temp:
- self.function.generate_disposal_code(code)
- self.function.free_temps(code)
- else:
- code.put_decref_clear(function, py_object_type)
- code.funcstate.release_temp(function)
- class InlinedDefNodeCallNode(CallNode):
- # Inline call to defnode
- #
- # function PyCFunctionNode
- # function_name NameNode
- # args [ExprNode]
- subexprs = ['args', 'function_name']
- is_temp = 1
- type = py_object_type
- function = None
- function_name = None
- def can_be_inlined(self):
- func_type= self.function.def_node
- if func_type.star_arg or func_type.starstar_arg:
- return False
- if len(func_type.args) != len(self.args):
- return False
- if func_type.num_kwonly_args:
- return False # actually wrong number of arguments
- return True
- def analyse_types(self, env):
- self.function_name = self.function_name.analyse_types(env)
- self.args = [ arg.analyse_types(env) for arg in self.args ]
- func_type = self.function.def_node
- actual_nargs = len(self.args)
- # Coerce arguments
- some_args_in_temps = False
- for i in range(actual_nargs):
- formal_type = func_type.args[i].type
- arg = self.args[i].coerce_to(formal_type, env)
- if arg.is_temp:
- if i > 0:
- # first argument in temp doesn't impact subsequent arguments
- some_args_in_temps = True
- elif arg.type.is_pyobject and not env.nogil:
- if arg.nonlocally_immutable():
- # plain local variables are ok
- pass
- else:
- # we do not safely own the argument's reference,
- # but we must make sure it cannot be collected
- # before we return from the function, so we create
- # an owned temp reference to it
- if i > 0: # first argument doesn't matter
- some_args_in_temps = True
- arg = arg.coerce_to_temp(env)
- self.args[i] = arg
- if some_args_in_temps:
- # if some args are temps and others are not, they may get
- # constructed in the wrong order (temps first) => make
- # sure they are either all temps or all not temps (except
- # for the last argument, which is evaluated last in any
- # case)
- for i in range(actual_nargs-1):
- arg = self.args[i]
- if arg.nonlocally_immutable():
- # locals, C functions, unassignable types are safe.
- pass
- elif arg.type.is_cpp_class:
- # Assignment has side effects, avoid.
- pass
- elif env.nogil and arg.type.is_pyobject:
- # can't copy a Python reference into a temp in nogil
- # env (this is safe: a construction would fail in
- # nogil anyway)
- pass
- else:
- #self.args[i] = arg.coerce_to_temp(env)
- # instead: issue a warning
- if i > 0:
- warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
- break
- return self
- def generate_result_code(self, code):
- arg_code = [self.function_name.py_result()]
- func_type = self.function.def_node
- for arg, proto_arg in zip(self.args, func_type.args):
- if arg.type.is_pyobject:
- arg_code.append(arg.result_as(proto_arg.type))
- else:
- arg_code.append(arg.result())
- arg_code = ', '.join(arg_code)
- code.putln(
- "%s = %s(%s); %s" % (
- self.result(),
- self.function.def_node.entry.pyfunc_cname,
- arg_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class PythonCapiFunctionNode(ExprNode):
- subexprs = []
- def __init__(self, pos, py_name, cname, func_type, utility_code = None):
- ExprNode.__init__(self, pos, name=py_name, cname=cname,
- type=func_type, utility_code=utility_code)
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- if self.utility_code:
- code.globalstate.use_utility_code(self.utility_code)
- def calculate_result_code(self):
- return self.cname
- class PythonCapiCallNode(SimpleCallNode):
- # Python C-API Function call (only created in transforms)
- # By default, we assume that the call never returns None, as this
- # is true for most C-API functions in CPython. If this does not
- # apply to a call, set the following to True (or None to inherit
- # the default behaviour).
- may_return_none = False
- def __init__(self, pos, function_name, func_type,
- utility_code = None, py_name=None, **kwargs):
- self.type = func_type.return_type
- self.result_ctype = self.type
- self.function = PythonCapiFunctionNode(
- pos, py_name, function_name, func_type,
- utility_code = utility_code)
- # call this last so that we can override the constructed
- # attributes above with explicit keyword arguments if required
- SimpleCallNode.__init__(self, pos, **kwargs)
- class CachedBuiltinMethodCallNode(CallNode):
- # Python call to a method of a known Python builtin (only created in transforms)
- subexprs = ['obj', 'args']
- is_temp = True
- def __init__(self, call_node, obj, method_name, args):
- super(CachedBuiltinMethodCallNode, self).__init__(
- call_node.pos,
- obj=obj, method_name=method_name, args=args,
- may_return_none=call_node.may_return_none,
- type=call_node.type)
- def may_be_none(self):
- if self.may_return_none is not None:
- return self.may_return_none
- return ExprNode.may_be_none(self)
- def generate_result_code(self, code):
- type_cname = self.obj.type.cname
- obj_cname = self.obj.py_result()
- args = [arg.py_result() for arg in self.args]
- call_code = code.globalstate.cached_unbound_method_call_code(
- obj_cname, type_cname, self.method_name, args)
- code.putln("%s = %s; %s" % (
- self.result(), call_code,
- code.error_goto_if_null(self.result(), self.pos)
- ))
- code.put_gotref(self.result())
- class GeneralCallNode(CallNode):
- # General Python function call, including keyword,
- # * and ** arguments.
- #
- # function ExprNode
- # positional_args ExprNode Tuple of positional arguments
- # keyword_args ExprNode or None Dict of keyword arguments
- type = py_object_type
- subexprs = ['function', 'positional_args', 'keyword_args']
- nogil_check = Node.gil_error
- def compile_time_value(self, denv):
- function = self.function.compile_time_value(denv)
- positional_args = self.positional_args.compile_time_value(denv)
- keyword_args = self.keyword_args.compile_time_value(denv)
- try:
- return function(*positional_args, **keyword_args)
- except Exception as e:
- self.compile_time_value_error(e)
- def explicit_args_kwds(self):
- if (self.keyword_args and not self.keyword_args.is_dict_literal or
- not self.positional_args.is_sequence_constructor):
- raise CompileError(self.pos,
- 'Compile-time keyword arguments must be explicit.')
- return self.positional_args.args, self.keyword_args
- def analyse_types(self, env):
- if self.analyse_as_type_constructor(env):
- return self
- self.function = self.function.analyse_types(env)
- if not self.function.type.is_pyobject:
- if self.function.type.is_error:
- self.type = error_type
- return self
- if hasattr(self.function, 'entry'):
- node = self.map_to_simple_call_node()
- if node is not None and node is not self:
- return node.analyse_types(env)
- elif self.function.entry.as_variable:
- self.function = self.function.coerce_to_pyobject(env)
- elif node is self:
- error(self.pos,
- "Non-trivial keyword arguments and starred "
- "arguments not allowed in cdef functions.")
- else:
- # error was already reported
- pass
- else:
- self.function = self.function.coerce_to_pyobject(env)
- if self.keyword_args:
- self.keyword_args = self.keyword_args.analyse_types(env)
- self.positional_args = self.positional_args.analyse_types(env)
- self.positional_args = \
- self.positional_args.coerce_to_pyobject(env)
- self.set_py_result_type(self.function)
- self.is_temp = 1
- return self
- def map_to_simple_call_node(self):
- """
- Tries to map keyword arguments to declared positional arguments.
- Returns self to try a Python call, None to report an error
- or a SimpleCallNode if the mapping succeeds.
- """
- if not isinstance(self.positional_args, TupleNode):
- # has starred argument
- return self
- if not self.keyword_args.is_dict_literal:
- # keywords come from arbitrary expression => nothing to do here
- return self
- function = self.function
- entry = getattr(function, 'entry', None)
- if not entry:
- return self
- function_type = entry.type
- if function_type.is_ptr:
- function_type = function_type.base_type
- if not function_type.is_cfunction:
- return self
- pos_args = self.positional_args.args
- kwargs = self.keyword_args
- declared_args = function_type.args
- if entry.is_cmethod:
- declared_args = declared_args[1:] # skip 'self'
- if len(pos_args) > len(declared_args):
- error(self.pos, "function call got too many positional arguments, "
- "expected %d, got %s" % (len(declared_args),
- len(pos_args)))
- return None
- matched_args = set([ arg.name for arg in declared_args[:len(pos_args)]
- if arg.name ])
- unmatched_args = declared_args[len(pos_args):]
- matched_kwargs_count = 0
- args = list(pos_args)
- # check for duplicate keywords
- seen = set(matched_args)
- has_errors = False
- for arg in kwargs.key_value_pairs:
- name = arg.key.value
- if name in seen:
- error(arg.pos, "argument '%s' passed twice" % name)
- has_errors = True
- # continue to report more errors if there are any
- seen.add(name)
- # match keywords that are passed in order
- for decl_arg, arg in zip(unmatched_args, kwargs.key_value_pairs):
- name = arg.key.value
- if decl_arg.name == name:
- matched_args.add(name)
- matched_kwargs_count += 1
- args.append(arg.value)
- else:
- break
- # match keyword arguments that are passed out-of-order, but keep
- # the evaluation of non-simple arguments in order by moving them
- # into temps
- from .UtilNodes import EvalWithTempExprNode, LetRefNode
- temps = []
- if len(kwargs.key_value_pairs) > matched_kwargs_count:
- unmatched_args = declared_args[len(args):]
- keywords = dict([ (arg.key.value, (i+len(pos_args), arg))
- for i, arg in enumerate(kwargs.key_value_pairs) ])
- first_missing_keyword = None
- for decl_arg in unmatched_args:
- name = decl_arg.name
- if name not in keywords:
- # missing keyword argument => either done or error
- if not first_missing_keyword:
- first_missing_keyword = name
- continue
- elif first_missing_keyword:
- if entry.as_variable:
- # we might be able to convert the function to a Python
- # object, which then allows full calling semantics
- # with default values in gaps - currently, we only
- # support optional arguments at the end
- return self
- # wasn't the last keyword => gaps are not supported
- error(self.pos, "C function call is missing "
- "argument '%s'" % first_missing_keyword)
- return None
- pos, arg = keywords[name]
- matched_args.add(name)
- matched_kwargs_count += 1
- if arg.value.is_simple():
- args.append(arg.value)
- else:
- temp = LetRefNode(arg.value)
- assert temp.is_simple()
- args.append(temp)
- temps.append((pos, temp))
- if temps:
- # may have to move preceding non-simple args into temps
- final_args = []
- new_temps = []
- first_temp_arg = temps[0][-1]
- for arg_value in args:
- if arg_value is first_temp_arg:
- break # done
- if arg_value.is_simple():
- final_args.append(arg_value)
- else:
- temp = LetRefNode(arg_value)
- new_temps.append(temp)
- final_args.append(temp)
- if new_temps:
- args = final_args
- temps = new_temps + [ arg for i,arg in sorted(temps) ]
- # check for unexpected keywords
- for arg in kwargs.key_value_pairs:
- name = arg.key.value
- if name not in matched_args:
- has_errors = True
- error(arg.pos,
- "C function got unexpected keyword argument '%s'" %
- name)
- if has_errors:
- # error was reported already
- return None
- # all keywords mapped to positional arguments
- # if we are missing arguments, SimpleCallNode will figure it out
- node = SimpleCallNode(self.pos, function=function, args=args)
- for temp in temps[::-1]:
- node = EvalWithTempExprNode(temp, node)
- return node
- def generate_result_code(self, code):
- if self.type.is_error: return
- if self.keyword_args:
- kwargs = self.keyword_args.py_result()
- else:
- kwargs = 'NULL'
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- "PyObjectCall", "ObjectHandling.c"))
- code.putln(
- "%s = __Pyx_PyObject_Call(%s, %s, %s); %s" % (
- self.result(),
- self.function.py_result(),
- self.positional_args.py_result(),
- kwargs,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class AsTupleNode(ExprNode):
- # Convert argument to tuple. Used for normalising
- # the * argument of a function call.
- #
- # arg ExprNode
- subexprs = ['arg']
- is_temp = 1
- def calculate_constant_result(self):
- self.constant_result = tuple(self.arg.constant_result)
- def compile_time_value(self, denv):
- arg = self.arg.compile_time_value(denv)
- try:
- return tuple(arg)
- except Exception as e:
- self.compile_time_value_error(e)
- def analyse_types(self, env):
- self.arg = self.arg.analyse_types(env).coerce_to_pyobject(env)
- if self.arg.type is tuple_type:
- return self.arg.as_none_safe_node("'NoneType' object is not iterable")
- self.type = tuple_type
- return self
- def may_be_none(self):
- return False
- nogil_check = Node.gil_error
- gil_message = "Constructing Python tuple"
- def generate_result_code(self, code):
- cfunc = "__Pyx_PySequence_Tuple" if self.arg.type in (py_object_type, tuple_type) else "PySequence_Tuple"
- code.putln(
- "%s = %s(%s); %s" % (
- self.result(),
- cfunc, self.arg.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class MergedDictNode(ExprNode):
- # Helper class for keyword arguments and other merged dicts.
- #
- # keyword_args [DictNode or other ExprNode]
- subexprs = ['keyword_args']
- is_temp = 1
- type = dict_type
- reject_duplicates = True
- def calculate_constant_result(self):
- result = {}
- reject_duplicates = self.reject_duplicates
- for item in self.keyword_args:
- if item.is_dict_literal:
- # process items in order
- items = ((key.constant_result, value.constant_result)
- for key, value in item.key_value_pairs)
- else:
- items = item.constant_result.iteritems()
- for key, value in items:
- if reject_duplicates and key in result:
- raise ValueError("duplicate keyword argument found: %s" % key)
- result[key] = value
- self.constant_result = result
- def compile_time_value(self, denv):
- result = {}
- reject_duplicates = self.reject_duplicates
- for item in self.keyword_args:
- if item.is_dict_literal:
- # process items in order
- items = [(key.compile_time_value(denv), value.compile_time_value(denv))
- for key, value in item.key_value_pairs]
- else:
- items = item.compile_time_value(denv).iteritems()
- try:
- for key, value in items:
- if reject_duplicates and key in result:
- raise ValueError("duplicate keyword argument found: %s" % key)
- result[key] = value
- except Exception as e:
- self.compile_time_value_error(e)
- return result
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- return dict_type
- def analyse_types(self, env):
- self.keyword_args = [
- arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node(
- # FIXME: CPython's error message starts with the runtime function name
- 'argument after ** must be a mapping, not NoneType')
- for arg in self.keyword_args
- ]
- return self
- def may_be_none(self):
- return False
- gil_message = "Constructing Python dict"
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- args = iter(self.keyword_args)
- item = next(args)
- item.generate_evaluation_code(code)
- if item.type is not dict_type:
- # CPython supports calling functions with non-dicts, so do we
- code.putln('if (likely(PyDict_CheckExact(%s))) {' %
- item.py_result())
- if item.is_dict_literal:
- item.make_owned_reference(code)
- code.putln("%s = %s;" % (self.result(), item.py_result()))
- item.generate_post_assignment_code(code)
- else:
- code.putln("%s = PyDict_Copy(%s); %s" % (
- self.result(),
- item.py_result(),
- code.error_goto_if_null(self.result(), item.pos)))
- code.put_gotref(self.result())
- item.generate_disposal_code(code)
- if item.type is not dict_type:
- code.putln('} else {')
- code.putln("%s = PyObject_CallFunctionObjArgs((PyObject*)&PyDict_Type, %s, NULL); %s" % (
- self.result(),
- item.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- item.generate_disposal_code(code)
- code.putln('}')
- item.free_temps(code)
- helpers = set()
- for item in args:
- if item.is_dict_literal:
- # inline update instead of creating an intermediate dict
- for arg in item.key_value_pairs:
- arg.generate_evaluation_code(code)
- if self.reject_duplicates:
- code.putln("if (unlikely(PyDict_Contains(%s, %s))) {" % (
- self.result(),
- arg.key.py_result()))
- helpers.add("RaiseDoubleKeywords")
- # FIXME: find out function name at runtime!
- code.putln('__Pyx_RaiseDoubleKeywordsError("function", %s); %s' % (
- arg.key.py_result(),
- code.error_goto(self.pos)))
- code.putln("}")
- code.put_error_if_neg(arg.key.pos, "PyDict_SetItem(%s, %s, %s)" % (
- self.result(),
- arg.key.py_result(),
- arg.value.py_result()))
- arg.generate_disposal_code(code)
- arg.free_temps(code)
- else:
- item.generate_evaluation_code(code)
- if self.reject_duplicates:
- # merge mapping into kwdict one by one as we need to check for duplicates
- helpers.add("MergeKeywords")
- code.put_error_if_neg(item.pos, "__Pyx_MergeKeywords(%s, %s)" % (
- self.result(), item.py_result()))
- else:
- # simple case, just add all entries
- helpers.add("RaiseMappingExpected")
- code.putln("if (unlikely(PyDict_Update(%s, %s) < 0)) {" % (
- self.result(), item.py_result()))
- code.putln("if (PyErr_ExceptionMatches(PyExc_AttributeError)) "
- "__Pyx_RaiseMappingExpectedError(%s);" % item.py_result())
- code.putln(code.error_goto(item.pos))
- code.putln("}")
- item.generate_disposal_code(code)
- item.free_temps(code)
- for helper in sorted(helpers):
- code.globalstate.use_utility_code(UtilityCode.load_cached(helper, "FunctionArguments.c"))
- def annotate(self, code):
- for item in self.keyword_args:
- item.annotate(code)
- class AttributeNode(ExprNode):
- # obj.attribute
- #
- # obj ExprNode
- # attribute string
- # needs_none_check boolean Used if obj is an extension type.
- # If set to True, it is known that the type is not None.
- #
- # Used internally:
- #
- # is_py_attr boolean Is a Python getattr operation
- # member string C name of struct member
- # is_called boolean Function call is being done on result
- # entry Entry Symbol table entry of attribute
- is_attribute = 1
- subexprs = ['obj']
- type = PyrexTypes.error_type
- entry = None
- is_called = 0
- needs_none_check = True
- is_memslice_transpose = False
- is_special_lookup = False
- is_py_attr = 0
- def as_cython_attribute(self):
- if (isinstance(self.obj, NameNode) and
- self.obj.is_cython_module and not
- self.attribute == u"parallel"):
- return self.attribute
- cy = self.obj.as_cython_attribute()
- if cy:
- return "%s.%s" % (cy, self.attribute)
- return None
- def coerce_to(self, dst_type, env):
- # If coercing to a generic pyobject and this is a cpdef function
- # we can create the corresponding attribute
- if dst_type is py_object_type:
- entry = self.entry
- if entry and entry.is_cfunction and entry.as_variable:
- # must be a cpdef function
- self.is_temp = 1
- self.entry = entry.as_variable
- self.analyse_as_python_attribute(env)
- return self
- return ExprNode.coerce_to(self, dst_type, env)
- def calculate_constant_result(self):
- attr = self.attribute
- if attr.startswith("__") and attr.endswith("__"):
- return
- self.constant_result = getattr(self.obj.constant_result, attr)
- def compile_time_value(self, denv):
- attr = self.attribute
- if attr.startswith("__") and attr.endswith("__"):
- error(self.pos,
- "Invalid attribute name '%s' in compile-time expression" % attr)
- return None
- obj = self.obj.compile_time_value(denv)
- try:
- return getattr(obj, attr)
- except Exception as e:
- self.compile_time_value_error(e)
- def type_dependencies(self, env):
- return self.obj.type_dependencies(env)
- def infer_type(self, env):
- # FIXME: this is way too redundant with analyse_types()
- node = self.analyse_as_cimported_attribute_node(env, target=False)
- if node is not None:
- if node.entry.type and node.entry.type.is_cfunction:
- # special-case - function converted to pointer
- return PyrexTypes.CPtrType(node.entry.type)
- else:
- return node.entry.type
- node = self.analyse_as_type_attribute(env)
- if node is not None:
- return node.entry.type
- obj_type = self.obj.infer_type(env)
- self.analyse_attribute(env, obj_type=obj_type)
- if obj_type.is_builtin_type and self.type.is_cfunction:
- # special case: C-API replacements for C methods of
- # builtin types cannot be inferred as C functions as
- # that would prevent their use as bound methods
- return py_object_type
- elif self.entry and self.entry.is_cmethod:
- # special case: bound methods should not be inferred
- # as their unbound method types
- return py_object_type
- return self.type
- def analyse_target_declaration(self, env):
- pass
- def analyse_target_types(self, env):
- node = self.analyse_types(env, target = 1)
- if node.type.is_const:
- error(self.pos, "Assignment to const attribute '%s'" % self.attribute)
- if not node.is_lvalue():
- error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type)
- return node
- def analyse_types(self, env, target = 0):
- self.initialized_check = env.directives['initializedcheck']
- node = self.analyse_as_cimported_attribute_node(env, target)
- if node is None and not target:
- node = self.analyse_as_type_attribute(env)
- if node is None:
- node = self.analyse_as_ordinary_attribute_node(env, target)
- assert node is not None
- if node.entry:
- node.entry.used = True
- if node.is_attribute:
- node.wrap_obj_in_nonecheck(env)
- return node
- def analyse_as_cimported_attribute_node(self, env, target):
- # Try to interpret this as a reference to an imported
- # C const, type, var or function. If successful, mutates
- # this node into a NameNode and returns 1, otherwise
- # returns 0.
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- entry = module_scope.lookup_here(self.attribute)
- if entry and (
- entry.is_cglobal or entry.is_cfunction
- or entry.is_type or entry.is_const):
- return self.as_name_node(env, entry, target)
- if self.is_cimported_module_without_shadow(env):
- error(self.pos, "cimported module has no attribute '%s'" % self.attribute)
- return self
- return None
- def analyse_as_type_attribute(self, env):
- # Try to interpret this as a reference to an unbound
- # C method of an extension type or builtin type. If successful,
- # creates a corresponding NameNode and returns it, otherwise
- # returns None.
- if self.obj.is_string_literal:
- return
- type = self.obj.analyse_as_type(env)
- if type:
- if type.is_extension_type or type.is_builtin_type or type.is_cpp_class:
- entry = type.scope.lookup_here(self.attribute)
- if entry and (entry.is_cmethod or type.is_cpp_class and entry.type.is_cfunction):
- if type.is_builtin_type:
- if not self.is_called:
- # must handle this as Python object
- return None
- ubcm_entry = entry
- else:
- # Create a temporary entry describing the C method
- # as an ordinary function.
- if entry.func_cname and not hasattr(entry.type, 'op_arg_struct'):
- cname = entry.func_cname
- if entry.type.is_static_method or (
- env.parent_scope and env.parent_scope.is_cpp_class_scope):
- ctype = entry.type
- elif type.is_cpp_class:
- error(self.pos, "%s not a static member of %s" % (entry.name, type))
- ctype = PyrexTypes.error_type
- else:
- # Fix self type.
- ctype = copy.copy(entry.type)
- ctype.args = ctype.args[:]
- ctype.args[0] = PyrexTypes.CFuncTypeArg('self', type, 'self', None)
- else:
- cname = "%s->%s" % (type.vtabptr_cname, entry.cname)
- ctype = entry.type
- ubcm_entry = Symtab.Entry(entry.name, cname, ctype)
- ubcm_entry.is_cfunction = 1
- ubcm_entry.func_cname = entry.func_cname
- ubcm_entry.is_unbound_cmethod = 1
- ubcm_entry.scope = entry.scope
- return self.as_name_node(env, ubcm_entry, target=False)
- elif type.is_enum:
- if self.attribute in type.values:
- for entry in type.entry.enum_values:
- if entry.name == self.attribute:
- return self.as_name_node(env, entry, target=False)
- else:
- error(self.pos, "%s not a known value of %s" % (self.attribute, type))
- else:
- error(self.pos, "%s not a known value of %s" % (self.attribute, type))
- return None
- def analyse_as_type(self, env):
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- return module_scope.lookup_type(self.attribute)
- if not self.obj.is_string_literal:
- base_type = self.obj.analyse_as_type(env)
- if base_type and hasattr(base_type, 'scope') and base_type.scope is not None:
- return base_type.scope.lookup_type(self.attribute)
- return None
- def analyse_as_extension_type(self, env):
- # Try to interpret this as a reference to an extension type
- # in a cimported module. Returns the extension type, or None.
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- entry = module_scope.lookup_here(self.attribute)
- if entry and entry.is_type:
- if entry.type.is_extension_type or entry.type.is_builtin_type:
- return entry.type
- return None
- def analyse_as_module(self, env):
- # Try to interpret this as a reference to a cimported module
- # in another cimported module. Returns the module scope, or None.
- module_scope = self.obj.analyse_as_module(env)
- if module_scope:
- entry = module_scope.lookup_here(self.attribute)
- if entry and entry.as_module:
- return entry.as_module
- return None
- def as_name_node(self, env, entry, target):
- # Create a corresponding NameNode from this node and complete the
- # analyse_types phase.
- node = NameNode.from_node(self, name=self.attribute, entry=entry)
- if target:
- node = node.analyse_target_types(env)
- else:
- node = node.analyse_rvalue_entry(env)
- node.entry.used = 1
- return node
- def analyse_as_ordinary_attribute_node(self, env, target):
- self.obj = self.obj.analyse_types(env)
- self.analyse_attribute(env)
- if self.entry and self.entry.is_cmethod and not self.is_called:
- # error(self.pos, "C method can only be called")
- pass
- ## Reference to C array turns into pointer to first element.
- #while self.type.is_array:
- # self.type = self.type.element_ptr_type()
- if self.is_py_attr:
- if not target:
- self.is_temp = 1
- self.result_ctype = py_object_type
- elif target and self.obj.type.is_builtin_type:
- error(self.pos, "Assignment to an immutable object field")
- #elif self.type.is_memoryviewslice and not target:
- # self.is_temp = True
- return self
- def analyse_attribute(self, env, obj_type = None):
- # Look up attribute and set self.type and self.member.
- immutable_obj = obj_type is not None # used during type inference
- self.is_py_attr = 0
- self.member = self.attribute
- if obj_type is None:
- if self.obj.type.is_string or self.obj.type.is_pyunicode_ptr:
- self.obj = self.obj.coerce_to_pyobject(env)
- obj_type = self.obj.type
- else:
- if obj_type.is_string or obj_type.is_pyunicode_ptr:
- obj_type = py_object_type
- if obj_type.is_ptr or obj_type.is_array:
- obj_type = obj_type.base_type
- self.op = "->"
- elif obj_type.is_extension_type or obj_type.is_builtin_type:
- self.op = "->"
- elif obj_type.is_reference and obj_type.is_fake_reference:
- self.op = "->"
- else:
- self.op = "."
- if obj_type.has_attributes:
- if obj_type.attributes_known():
- entry = obj_type.scope.lookup_here(self.attribute)
- if obj_type.is_memoryviewslice and not entry:
- if self.attribute == 'T':
- self.is_memslice_transpose = True
- self.is_temp = True
- self.use_managed_ref = True
- self.type = self.obj.type.transpose(self.pos)
- return
- else:
- obj_type.declare_attribute(self.attribute, env, self.pos)
- entry = obj_type.scope.lookup_here(self.attribute)
- if entry and entry.is_member:
- entry = None
- else:
- error(self.pos,
- "Cannot select attribute of incomplete type '%s'"
- % obj_type)
- self.type = PyrexTypes.error_type
- return
- self.entry = entry
- if entry:
- if obj_type.is_extension_type and entry.name == "__weakref__":
- error(self.pos, "Illegal use of special attribute __weakref__")
- # def methods need the normal attribute lookup
- # because they do not have struct entries
- # fused function go through assignment synthesis
- # (foo = pycfunction(foo_func_obj)) and need to go through
- # regular Python lookup as well
- if (entry.is_variable and not entry.fused_cfunction) or entry.is_cmethod:
- self.type = entry.type
- self.member = entry.cname
- return
- else:
- # If it's not a variable or C method, it must be a Python
- # method of an extension type, so we treat it like a Python
- # attribute.
- pass
- # If we get here, the base object is not a struct/union/extension
- # type, or it is an extension type and the attribute is either not
- # declared or is declared as a Python method. Treat it as a Python
- # attribute reference.
- self.analyse_as_python_attribute(env, obj_type, immutable_obj)
- def analyse_as_python_attribute(self, env, obj_type=None, immutable_obj=False):
- if obj_type is None:
- obj_type = self.obj.type
- # mangle private '__*' Python attributes used inside of a class
- self.attribute = env.mangle_class_private_name(self.attribute)
- self.member = self.attribute
- self.type = py_object_type
- self.is_py_attr = 1
- if not obj_type.is_pyobject and not obj_type.is_error:
- # Expose python methods for immutable objects.
- if (obj_type.is_string or obj_type.is_cpp_string
- or obj_type.is_buffer or obj_type.is_memoryviewslice
- or obj_type.is_numeric
- or (obj_type.is_ctuple and obj_type.can_coerce_to_pyobject(env))
- or (obj_type.is_struct and obj_type.can_coerce_to_pyobject(env))):
- if not immutable_obj:
- self.obj = self.obj.coerce_to_pyobject(env)
- elif (obj_type.is_cfunction and (self.obj.is_name or self.obj.is_attribute)
- and self.obj.entry.as_variable
- and self.obj.entry.as_variable.type.is_pyobject):
- # might be an optimised builtin function => unpack it
- if not immutable_obj:
- self.obj = self.obj.coerce_to_pyobject(env)
- else:
- error(self.pos,
- "Object of type '%s' has no attribute '%s'" %
- (obj_type, self.attribute))
- def wrap_obj_in_nonecheck(self, env):
- if not env.directives['nonecheck']:
- return
- msg = None
- format_args = ()
- if (self.obj.type.is_extension_type and self.needs_none_check and not
- self.is_py_attr):
- msg = "'NoneType' object has no attribute '%{0}s'".format('.30' if len(self.attribute) <= 30 else '')
- format_args = (self.attribute,)
- elif self.obj.type.is_memoryviewslice:
- if self.is_memslice_transpose:
- msg = "Cannot transpose None memoryview slice"
- else:
- entry = self.obj.type.scope.lookup_here(self.attribute)
- if entry:
- # copy/is_c_contig/shape/strides etc
- msg = "Cannot access '%s' attribute of None memoryview slice"
- format_args = (entry.name,)
- if msg:
- self.obj = self.obj.as_none_safe_node(msg, 'PyExc_AttributeError',
- format_args=format_args)
- def nogil_check(self, env):
- if self.is_py_attr:
- self.gil_error()
- gil_message = "Accessing Python attribute"
- def is_cimported_module_without_shadow(self, env):
- return self.obj.is_cimported_module_without_shadow(env)
- def is_simple(self):
- if self.obj:
- return self.result_in_temp() or self.obj.is_simple()
- else:
- return NameNode.is_simple(self)
- def is_lvalue(self):
- if self.obj:
- return True
- else:
- return NameNode.is_lvalue(self)
- def is_ephemeral(self):
- if self.obj:
- return self.obj.is_ephemeral()
- else:
- return NameNode.is_ephemeral(self)
- def calculate_result_code(self):
- #print "AttributeNode.calculate_result_code:", self.member ###
- #print "...obj node =", self.obj, "code", self.obj.result() ###
- #print "...obj type", self.obj.type, "ctype", self.obj.ctype() ###
- obj = self.obj
- obj_code = obj.result_as(obj.type)
- #print "...obj_code =", obj_code ###
- if self.entry and self.entry.is_cmethod:
- if obj.type.is_extension_type and not self.entry.is_builtin_cmethod:
- if self.entry.final_func_cname:
- return self.entry.final_func_cname
- if self.type.from_fused:
- # If the attribute was specialized through indexing, make
- # sure to get the right fused name, as our entry was
- # replaced by our parent index node
- # (AnalyseExpressionsTransform)
- self.member = self.entry.cname
- return "((struct %s *)%s%s%s)->%s" % (
- obj.type.vtabstruct_cname, obj_code, self.op,
- obj.type.vtabslot_cname, self.member)
- elif self.result_is_used:
- return self.member
- # Generating no code at all for unused access to optimised builtin
- # methods fixes the problem that some optimisations only exist as
- # macros, i.e. there is no function pointer to them, so we would
- # generate invalid C code here.
- return
- elif obj.type.is_complex:
- return "__Pyx_C%s(%s)" % (self.member.upper(), obj_code)
- else:
- if obj.type.is_builtin_type and self.entry and self.entry.is_variable:
- # accessing a field of a builtin type, need to cast better than result_as() does
- obj_code = obj.type.cast_code(obj.result(), to_object_struct = True)
- return "%s%s%s" % (obj_code, self.op, self.member)
- def generate_result_code(self, code):
- if self.is_py_attr:
- if self.is_special_lookup:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectLookupSpecial", "ObjectHandling.c"))
- lookup_func_name = '__Pyx_PyObject_LookupSpecial'
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c"))
- lookup_func_name = '__Pyx_PyObject_GetAttrStr'
- code.putln(
- '%s = %s(%s, %s); %s' % (
- self.result(),
- lookup_func_name,
- self.obj.py_result(),
- code.intern_identifier(self.attribute),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif self.type.is_memoryviewslice:
- if self.is_memslice_transpose:
- # transpose the slice
- for access, packing in self.type.axes:
- if access == 'ptr':
- error(self.pos, "Transposing not supported for slices "
- "with indirect dimensions")
- return
- code.putln("%s = %s;" % (self.result(), self.obj.result()))
- code.put_incref_memoryviewslice(self.result(), have_gil=True)
- T = "__pyx_memslice_transpose(&%s) == 0"
- code.putln(code.error_goto_if(T % self.result(), self.pos))
- elif self.initialized_check:
- code.putln(
- 'if (unlikely(!%s.memview)) {'
- 'PyErr_SetString(PyExc_AttributeError,'
- '"Memoryview is not initialized");'
- '%s'
- '}' % (self.result(), code.error_goto(self.pos)))
- else:
- # result_code contains what is needed, but we may need to insert
- # a check and raise an exception
- if self.obj.type and self.obj.type.is_extension_type:
- pass
- elif self.entry and self.entry.is_cmethod:
- # C method implemented as function call with utility code
- code.globalstate.use_entry_utility_code(self.entry)
- def generate_disposal_code(self, code):
- if self.is_temp and self.type.is_memoryviewslice and self.is_memslice_transpose:
- # mirror condition for putting the memview incref here:
- code.put_xdecref_memoryviewslice(
- self.result(), have_gil=True)
- code.putln("%s.memview = NULL;" % self.result())
- code.putln("%s.data = NULL;" % self.result())
- else:
- ExprNode.generate_disposal_code(self, code)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- self.obj.generate_evaluation_code(code)
- if self.is_py_attr:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
- code.put_error_if_neg(self.pos,
- '__Pyx_PyObject_SetAttrStr(%s, %s, %s)' % (
- self.obj.py_result(),
- code.intern_identifier(self.attribute),
- rhs.py_result()))
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- elif self.obj.type.is_complex:
- code.putln("__Pyx_SET_C%s(%s, %s);" % (
- self.member.upper(),
- self.obj.result_as(self.obj.type),
- rhs.result_as(self.ctype())))
- rhs.generate_disposal_code(code)
- rhs.free_temps(code)
- else:
- select_code = self.result()
- if self.type.is_pyobject and self.use_managed_ref:
- rhs.make_owned_reference(code)
- code.put_giveref(rhs.py_result())
- code.put_gotref(select_code)
- code.put_decref(select_code, self.ctype())
- elif self.type.is_memoryviewslice:
- from . import MemoryView
- MemoryView.put_assign_to_memviewslice(
- select_code, rhs, rhs.result(), self.type, code)
- if not self.type.is_memoryviewslice:
- code.putln(
- "%s = %s;" % (
- select_code,
- rhs.result_as(self.ctype())))
- #rhs.result()))
- rhs.generate_post_assignment_code(code)
- rhs.free_temps(code)
- self.obj.generate_disposal_code(code)
- self.obj.free_temps(code)
- def generate_deletion_code(self, code, ignore_nonexisting=False):
- self.obj.generate_evaluation_code(code)
- if self.is_py_attr or (self.entry.scope.is_property_scope
- and u'__del__' in self.entry.scope.entries):
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyObjectSetAttrStr", "ObjectHandling.c"))
- code.put_error_if_neg(self.pos,
- '__Pyx_PyObject_DelAttrStr(%s, %s)' % (
- self.obj.py_result(),
- code.intern_identifier(self.attribute)))
- else:
- error(self.pos, "Cannot delete C attribute of extension type")
- self.obj.generate_disposal_code(code)
- self.obj.free_temps(code)
- def annotate(self, code):
- if self.is_py_attr:
- style, text = 'py_attr', 'python attribute (%s)'
- else:
- style, text = 'c_attr', 'c attribute (%s)'
- code.annotate(self.pos, AnnotationItem(style, text % self.type, size=len(self.attribute)))
- #-------------------------------------------------------------------
- #
- # Constructor nodes
- #
- #-------------------------------------------------------------------
- class StarredUnpackingNode(ExprNode):
- # A starred expression like "*a"
- #
- # This is only allowed in sequence assignment or construction such as
- #
- # a, *b = (1,2,3,4) => a = 1 ; b = [2,3,4]
- #
- # and will be special cased during type analysis (or generate an error
- # if it's found at unexpected places).
- #
- # target ExprNode
- subexprs = ['target']
- is_starred = 1
- type = py_object_type
- is_temp = 1
- starred_expr_allowed_here = False
- def __init__(self, pos, target):
- ExprNode.__init__(self, pos, target=target)
- def analyse_declarations(self, env):
- if not self.starred_expr_allowed_here:
- error(self.pos, "starred expression is not allowed here")
- self.target.analyse_declarations(env)
- def infer_type(self, env):
- return self.target.infer_type(env)
- def analyse_types(self, env):
- if not self.starred_expr_allowed_here:
- error(self.pos, "starred expression is not allowed here")
- self.target = self.target.analyse_types(env)
- self.type = self.target.type
- return self
- def analyse_target_declaration(self, env):
- self.target.analyse_target_declaration(env)
- def analyse_target_types(self, env):
- self.target = self.target.analyse_target_types(env)
- self.type = self.target.type
- return self
- def calculate_result_code(self):
- return ""
- def generate_result_code(self, code):
- pass
- class SequenceNode(ExprNode):
- # Base class for list and tuple constructor nodes.
- # Contains common code for performing sequence unpacking.
- #
- # args [ExprNode]
- # unpacked_items [ExprNode] or None
- # coerced_unpacked_items [ExprNode] or None
- # mult_factor ExprNode the integer number of content repetitions ([1,2]*3)
- subexprs = ['args', 'mult_factor']
- is_sequence_constructor = 1
- unpacked_items = None
- mult_factor = None
- slow = False # trade speed for code size (e.g. use PyTuple_Pack())
- def compile_time_value_list(self, denv):
- return [arg.compile_time_value(denv) for arg in self.args]
- def replace_starred_target_node(self):
- # replace a starred node in the targets by the contained expression
- self.starred_assignment = False
- args = []
- for arg in self.args:
- if arg.is_starred:
- if self.starred_assignment:
- error(arg.pos, "more than 1 starred expression in assignment")
- self.starred_assignment = True
- arg = arg.target
- arg.is_starred = True
- args.append(arg)
- self.args = args
- def analyse_target_declaration(self, env):
- self.replace_starred_target_node()
- for arg in self.args:
- arg.analyse_target_declaration(env)
- def analyse_types(self, env, skip_children=False):
- for i, arg in enumerate(self.args):
- if not skip_children:
- arg = arg.analyse_types(env)
- self.args[i] = arg.coerce_to_pyobject(env)
- if self.mult_factor:
- self.mult_factor = self.mult_factor.analyse_types(env)
- if not self.mult_factor.type.is_int:
- self.mult_factor = self.mult_factor.coerce_to_pyobject(env)
- self.is_temp = 1
- # not setting self.type here, subtypes do this
- return self
- def coerce_to_ctuple(self, dst_type, env):
- if self.type == dst_type:
- return self
- assert not self.mult_factor
- if len(self.args) != dst_type.size:
- error(self.pos, "trying to coerce sequence to ctuple of wrong length, expected %d, got %d" % (
- dst_type.size, len(self.args)))
- coerced_args = [arg.coerce_to(type, env) for arg, type in zip(self.args, dst_type.components)]
- return TupleNode(self.pos, args=coerced_args, type=dst_type, is_temp=True)
- def _create_merge_node_if_necessary(self, env):
- self._flatten_starred_args()
- if not any(arg.is_starred for arg in self.args):
- return self
- # convert into MergedSequenceNode by building partial sequences
- args = []
- values = []
- for arg in self.args:
- if arg.is_starred:
- if values:
- args.append(TupleNode(values[0].pos, args=values).analyse_types(env, skip_children=True))
- values = []
- args.append(arg.target)
- else:
- values.append(arg)
- if values:
- args.append(TupleNode(values[0].pos, args=values).analyse_types(env, skip_children=True))
- node = MergedSequenceNode(self.pos, args, self.type)
- if self.mult_factor:
- node = binop_node(
- self.pos, '*', node, self.mult_factor.coerce_to_pyobject(env),
- inplace=True, type=self.type, is_temp=True)
- return node
- def _flatten_starred_args(self):
- args = []
- for arg in self.args:
- if arg.is_starred and arg.target.is_sequence_constructor and not arg.target.mult_factor:
- args.extend(arg.target.args)
- else:
- args.append(arg)
- self.args[:] = args
- def may_be_none(self):
- return False
- def analyse_target_types(self, env):
- if self.mult_factor:
- error(self.pos, "can't assign to multiplied sequence")
- self.unpacked_items = []
- self.coerced_unpacked_items = []
- self.any_coerced_items = False
- for i, arg in enumerate(self.args):
- arg = self.args[i] = arg.analyse_target_types(env)
- if arg.is_starred:
- if not arg.type.assignable_from(list_type):
- error(arg.pos,
- "starred target must have Python object (list) type")
- if arg.type is py_object_type:
- arg.type = list_type
- unpacked_item = PyTempNode(self.pos, env)
- coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env)
- if unpacked_item is not coerced_unpacked_item:
- self.any_coerced_items = True
- self.unpacked_items.append(unpacked_item)
- self.coerced_unpacked_items.append(coerced_unpacked_item)
- self.type = py_object_type
- return self
- def generate_result_code(self, code):
- self.generate_operation_code(code)
- def generate_sequence_packing_code(self, code, target=None, plain=False):
- if target is None:
- target = self.result()
- size_factor = c_mult = ''
- mult_factor = None
- if self.mult_factor and not plain:
- mult_factor = self.mult_factor
- if mult_factor.type.is_int:
- c_mult = mult_factor.result()
- if (isinstance(mult_factor.constant_result, _py_int_types) and
- mult_factor.constant_result > 0):
- size_factor = ' * %s' % mult_factor.constant_result
- elif mult_factor.type.signed:
- size_factor = ' * ((%s<0) ? 0:%s)' % (c_mult, c_mult)
- else:
- size_factor = ' * (%s)' % (c_mult,)
- if self.type is tuple_type and (self.is_literal or self.slow) and not c_mult:
- # use PyTuple_Pack() to avoid generating huge amounts of one-time code
- code.putln('%s = PyTuple_Pack(%d, %s); %s' % (
- target,
- len(self.args),
- ', '.join(arg.py_result() for arg in self.args),
- code.error_goto_if_null(target, self.pos)))
- code.put_gotref(target)
- elif self.type.is_ctuple:
- for i, arg in enumerate(self.args):
- code.putln("%s.f%s = %s;" % (
- target, i, arg.result()))
- else:
- # build the tuple/list step by step, potentially multiplying it as we go
- if self.type is list_type:
- create_func, set_item_func = 'PyList_New', 'PyList_SET_ITEM'
- elif self.type is tuple_type:
- create_func, set_item_func = 'PyTuple_New', 'PyTuple_SET_ITEM'
- else:
- raise InternalError("sequence packing for unexpected type %s" % self.type)
- arg_count = len(self.args)
- code.putln("%s = %s(%s%s); %s" % (
- target, create_func, arg_count, size_factor,
- code.error_goto_if_null(target, self.pos)))
- code.put_gotref(target)
- if c_mult:
- # FIXME: can't use a temp variable here as the code may
- # end up in the constant building function. Temps
- # currently don't work there.
- #counter = code.funcstate.allocate_temp(mult_factor.type, manage_ref=False)
- counter = Naming.quick_temp_cname
- code.putln('{ Py_ssize_t %s;' % counter)
- if arg_count == 1:
- offset = counter
- else:
- offset = '%s * %s' % (counter, arg_count)
- code.putln('for (%s=0; %s < %s; %s++) {' % (
- counter, counter, c_mult, counter
- ))
- else:
- offset = ''
- for i in range(arg_count):
- arg = self.args[i]
- if c_mult or not arg.result_in_temp():
- code.put_incref(arg.result(), arg.ctype())
- code.put_giveref(arg.py_result())
- code.putln("%s(%s, %s, %s);" % (
- set_item_func,
- target,
- (offset and i) and ('%s + %s' % (offset, i)) or (offset or i),
- arg.py_result()))
- if c_mult:
- code.putln('}')
- #code.funcstate.release_temp(counter)
- code.putln('}')
- if mult_factor is not None and mult_factor.type.is_pyobject:
- code.putln('{ PyObject* %s = PyNumber_InPlaceMultiply(%s, %s); %s' % (
- Naming.quick_temp_cname, target, mult_factor.py_result(),
- code.error_goto_if_null(Naming.quick_temp_cname, self.pos)
- ))
- code.put_gotref(Naming.quick_temp_cname)
- code.put_decref(target, py_object_type)
- code.putln('%s = %s;' % (target, Naming.quick_temp_cname))
- code.putln('}')
- def generate_subexpr_disposal_code(self, code):
- if self.mult_factor and self.mult_factor.type.is_int:
- super(SequenceNode, self).generate_subexpr_disposal_code(code)
- elif self.type is tuple_type and (self.is_literal or self.slow):
- super(SequenceNode, self).generate_subexpr_disposal_code(code)
- else:
- # We call generate_post_assignment_code here instead
- # of generate_disposal_code, because values were stored
- # in the tuple using a reference-stealing operation.
- for arg in self.args:
- arg.generate_post_assignment_code(code)
- # Should NOT call free_temps -- this is invoked by the default
- # generate_evaluation_code which will do that.
- if self.mult_factor:
- self.mult_factor.generate_disposal_code(code)
- def generate_assignment_code(self, rhs, code, overloaded_assignment=False,
- exception_check=None, exception_value=None):
- if self.starred_assignment:
- self.generate_starred_assignment_code(rhs, code)
- else:
- self.generate_parallel_assignment_code(rhs, code)
- for item in self.unpacked_items:
- item.release(code)
- rhs.free_temps(code)
- _func_iternext_type = PyrexTypes.CPtrType(PyrexTypes.CFuncType(
- PyrexTypes.py_object_type, [
- PyrexTypes.CFuncTypeArg("it", PyrexTypes.py_object_type, None),
- ]))
- def generate_parallel_assignment_code(self, rhs, code):
- # Need to work around the fact that generate_evaluation_code
- # allocates the temps in a rather hacky way -- the assignment
- # is evaluated twice, within each if-block.
- for item in self.unpacked_items:
- item.allocate(code)
- special_unpack = (rhs.type is py_object_type
- or rhs.type in (tuple_type, list_type)
- or not rhs.type.is_builtin_type)
- long_enough_for_a_loop = len(self.unpacked_items) > 3
- if special_unpack:
- self.generate_special_parallel_unpacking_code(
- code, rhs, use_loop=long_enough_for_a_loop)
- else:
- code.putln("{")
- self.generate_generic_parallel_unpacking_code(
- code, rhs, self.unpacked_items, use_loop=long_enough_for_a_loop)
- code.putln("}")
- for value_node in self.coerced_unpacked_items:
- value_node.generate_evaluation_code(code)
- for i in range(len(self.args)):
- self.args[i].generate_assignment_code(
- self.coerced_unpacked_items[i], code)
- def generate_special_parallel_unpacking_code(self, code, rhs, use_loop):
- sequence_type_test = '1'
- none_check = "likely(%s != Py_None)" % rhs.py_result()
- if rhs.type is list_type:
- sequence_types = ['List']
- if rhs.may_be_none():
- sequence_type_test = none_check
- elif rhs.type is tuple_type:
- sequence_types = ['Tuple']
- if rhs.may_be_none():
- sequence_type_test = none_check
- else:
- sequence_types = ['Tuple', 'List']
- tuple_check = 'likely(PyTuple_CheckExact(%s))' % rhs.py_result()
- list_check = 'PyList_CheckExact(%s)' % rhs.py_result()
- sequence_type_test = "(%s) || (%s)" % (tuple_check, list_check)
- code.putln("if (%s) {" % sequence_type_test)
- code.putln("PyObject* sequence = %s;" % rhs.py_result())
- # list/tuple => check size
- code.putln("Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);")
- code.putln("if (unlikely(size != %d)) {" % len(self.args))
- code.globalstate.use_utility_code(raise_too_many_values_to_unpack)
- code.putln("if (size > %d) __Pyx_RaiseTooManyValuesError(%d);" % (
- len(self.args), len(self.args)))
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- code.putln("else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);")
- # < 0 => exception
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- code.putln("#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS")
- # unpack items from list/tuple in unrolled loop (can't fail)
- if len(sequence_types) == 2:
- code.putln("if (likely(Py%s_CheckExact(sequence))) {" % sequence_types[0])
- for i, item in enumerate(self.unpacked_items):
- code.putln("%s = Py%s_GET_ITEM(sequence, %d); " % (
- item.result(), sequence_types[0], i))
- if len(sequence_types) == 2:
- code.putln("} else {")
- for i, item in enumerate(self.unpacked_items):
- code.putln("%s = Py%s_GET_ITEM(sequence, %d); " % (
- item.result(), sequence_types[1], i))
- code.putln("}")
- for item in self.unpacked_items:
- code.put_incref(item.result(), item.ctype())
- code.putln("#else")
- # in non-CPython, use the PySequence protocol (which can fail)
- if not use_loop:
- for i, item in enumerate(self.unpacked_items):
- code.putln("%s = PySequence_ITEM(sequence, %d); %s" % (
- item.result(), i,
- code.error_goto_if_null(item.result(), self.pos)))
- code.put_gotref(item.result())
- else:
- code.putln("{")
- code.putln("Py_ssize_t i;")
- code.putln("PyObject** temps[%s] = {%s};" % (
- len(self.unpacked_items),
- ','.join(['&%s' % item.result() for item in self.unpacked_items])))
- code.putln("for (i=0; i < %s; i++) {" % len(self.unpacked_items))
- code.putln("PyObject* item = PySequence_ITEM(sequence, i); %s" % (
- code.error_goto_if_null('item', self.pos)))
- code.put_gotref('item')
- code.putln("*(temps[i]) = item;")
- code.putln("}")
- code.putln("}")
- code.putln("#endif")
- rhs.generate_disposal_code(code)
- if sequence_type_test == '1':
- code.putln("}") # all done
- elif sequence_type_test == none_check:
- # either tuple/list or None => save some code by generating the error directly
- code.putln("} else {")
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("RaiseNoneIterError", "ObjectHandling.c"))
- code.putln("__Pyx_RaiseNoneNotIterableError(); %s" % code.error_goto(self.pos))
- code.putln("}") # all done
- else:
- code.putln("} else {") # needs iteration fallback code
- self.generate_generic_parallel_unpacking_code(
- code, rhs, self.unpacked_items, use_loop=use_loop)
- code.putln("}")
- def generate_generic_parallel_unpacking_code(self, code, rhs, unpacked_items, use_loop, terminate=True):
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- code.globalstate.use_utility_code(UtilityCode.load_cached("IterFinish", "ObjectHandling.c"))
- code.putln("Py_ssize_t index = -1;") # must be at the start of a C block!
- if use_loop:
- code.putln("PyObject** temps[%s] = {%s};" % (
- len(self.unpacked_items),
- ','.join(['&%s' % item.result() for item in unpacked_items])))
- iterator_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln(
- "%s = PyObject_GetIter(%s); %s" % (
- iterator_temp,
- rhs.py_result(),
- code.error_goto_if_null(iterator_temp, self.pos)))
- code.put_gotref(iterator_temp)
- rhs.generate_disposal_code(code)
- iternext_func = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False)
- code.putln("%s = Py_TYPE(%s)->tp_iternext;" % (
- iternext_func, iterator_temp))
- unpacking_error_label = code.new_label('unpacking_failed')
- unpack_code = "%s(%s)" % (iternext_func, iterator_temp)
- if use_loop:
- code.putln("for (index=0; index < %s; index++) {" % len(unpacked_items))
- code.put("PyObject* item = %s; if (unlikely(!item)) " % unpack_code)
- code.put_goto(unpacking_error_label)
- code.put_gotref("item")
- code.putln("*(temps[index]) = item;")
- code.putln("}")
- else:
- for i, item in enumerate(unpacked_items):
- code.put(
- "index = %d; %s = %s; if (unlikely(!%s)) " % (
- i,
- item.result(),
- unpack_code,
- item.result()))
- code.put_goto(unpacking_error_label)
- code.put_gotref(item.py_result())
- if terminate:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("UnpackItemEndCheck", "ObjectHandling.c"))
- code.put_error_if_neg(self.pos, "__Pyx_IternextUnpackEndCheck(%s, %d)" % (
- unpack_code,
- len(unpacked_items)))
- code.putln("%s = NULL;" % iternext_func)
- code.put_decref_clear(iterator_temp, py_object_type)
- unpacking_done_label = code.new_label('unpacking_done')
- code.put_goto(unpacking_done_label)
- code.put_label(unpacking_error_label)
- code.put_decref_clear(iterator_temp, py_object_type)
- code.putln("%s = NULL;" % iternext_func)
- code.putln("if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);")
- code.putln(code.error_goto(self.pos))
- code.put_label(unpacking_done_label)
- code.funcstate.release_temp(iternext_func)
- if terminate:
- code.funcstate.release_temp(iterator_temp)
- iterator_temp = None
- return iterator_temp
- def generate_starred_assignment_code(self, rhs, code):
- for i, arg in enumerate(self.args):
- if arg.is_starred:
- starred_target = self.unpacked_items[i]
- unpacked_fixed_items_left = self.unpacked_items[:i]
- unpacked_fixed_items_right = self.unpacked_items[i+1:]
- break
- else:
- assert False
- iterator_temp = None
- if unpacked_fixed_items_left:
- for item in unpacked_fixed_items_left:
- item.allocate(code)
- code.putln('{')
- iterator_temp = self.generate_generic_parallel_unpacking_code(
- code, rhs, unpacked_fixed_items_left,
- use_loop=True, terminate=False)
- for i, item in enumerate(unpacked_fixed_items_left):
- value_node = self.coerced_unpacked_items[i]
- value_node.generate_evaluation_code(code)
- code.putln('}')
- starred_target.allocate(code)
- target_list = starred_target.result()
- code.putln("%s = PySequence_List(%s); %s" % (
- target_list,
- iterator_temp or rhs.py_result(),
- code.error_goto_if_null(target_list, self.pos)))
- code.put_gotref(target_list)
- if iterator_temp:
- code.put_decref_clear(iterator_temp, py_object_type)
- code.funcstate.release_temp(iterator_temp)
- else:
- rhs.generate_disposal_code(code)
- if unpacked_fixed_items_right:
- code.globalstate.use_utility_code(raise_need_more_values_to_unpack)
- length_temp = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, manage_ref=False)
- code.putln('%s = PyList_GET_SIZE(%s);' % (length_temp, target_list))
- code.putln("if (unlikely(%s < %d)) {" % (length_temp, len(unpacked_fixed_items_right)))
- code.putln("__Pyx_RaiseNeedMoreValuesError(%d+%s); %s" % (
- len(unpacked_fixed_items_left), length_temp,
- code.error_goto(self.pos)))
- code.putln('}')
- for item in unpacked_fixed_items_right[::-1]:
- item.allocate(code)
- for i, (item, coerced_arg) in enumerate(zip(unpacked_fixed_items_right[::-1],
- self.coerced_unpacked_items[::-1])):
- code.putln('#if CYTHON_COMPILING_IN_CPYTHON')
- code.putln("%s = PyList_GET_ITEM(%s, %s-%d); " % (
- item.py_result(), target_list, length_temp, i+1))
- # resize the list the hard way
- code.putln("((PyVarObject*)%s)->ob_size--;" % target_list)
- code.putln('#else')
- code.putln("%s = PySequence_ITEM(%s, %s-%d); " % (
- item.py_result(), target_list, length_temp, i+1))
- code.putln('#endif')
- code.put_gotref(item.py_result())
- coerced_arg.generate_evaluation_code(code)
- code.putln('#if !CYTHON_COMPILING_IN_CPYTHON')
- sublist_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True)
- code.putln('%s = PySequence_GetSlice(%s, 0, %s-%d); %s' % (
- sublist_temp, target_list, length_temp, len(unpacked_fixed_items_right),
- code.error_goto_if_null(sublist_temp, self.pos)))
- code.put_gotref(sublist_temp)
- code.funcstate.release_temp(length_temp)
- code.put_decref(target_list, py_object_type)
- code.putln('%s = %s; %s = NULL;' % (target_list, sublist_temp, sublist_temp))
- code.putln('#else')
- code.putln('(void)%s;' % sublist_temp) # avoid warning about unused variable
- code.funcstate.release_temp(sublist_temp)
- code.putln('#endif')
- for i, arg in enumerate(self.args):
- arg.generate_assignment_code(self.coerced_unpacked_items[i], code)
- def annotate(self, code):
- for arg in self.args:
- arg.annotate(code)
- if self.unpacked_items:
- for arg in self.unpacked_items:
- arg.annotate(code)
- for arg in self.coerced_unpacked_items:
- arg.annotate(code)
- class TupleNode(SequenceNode):
- # Tuple constructor.
- type = tuple_type
- is_partly_literal = False
- gil_message = "Constructing Python tuple"
- def infer_type(self, env):
- if self.mult_factor or not self.args:
- return tuple_type
- arg_types = [arg.infer_type(env) for arg in self.args]
- if any(type.is_pyobject or type.is_memoryviewslice or type.is_unspecified or type.is_fused
- for type in arg_types):
- return tuple_type
- return env.declare_tuple_type(self.pos, arg_types).type
- def analyse_types(self, env, skip_children=False):
- if len(self.args) == 0:
- self.is_temp = False
- self.is_literal = True
- return self
- if not skip_children:
- for i, arg in enumerate(self.args):
- if arg.is_starred:
- arg.starred_expr_allowed_here = True
- self.args[i] = arg.analyse_types(env)
- if (not self.mult_factor and
- not any((arg.is_starred or arg.type.is_pyobject or arg.type.is_memoryviewslice or arg.type.is_fused)
- for arg in self.args)):
- self.type = env.declare_tuple_type(self.pos, (arg.type for arg in self.args)).type
- self.is_temp = 1
- return self
- node = SequenceNode.analyse_types(self, env, skip_children=True)
- node = node._create_merge_node_if_necessary(env)
- if not node.is_sequence_constructor:
- return node
- if not all(child.is_literal for child in node.args):
- return node
- if not node.mult_factor or (
- node.mult_factor.is_literal and
- isinstance(node.mult_factor.constant_result, _py_int_types)):
- node.is_temp = False
- node.is_literal = True
- else:
- if not node.mult_factor.type.is_pyobject:
- node.mult_factor = node.mult_factor.coerce_to_pyobject(env)
- node.is_temp = True
- node.is_partly_literal = True
- return node
- def analyse_as_type(self, env):
- # ctuple type
- if not self.args:
- return None
- item_types = [arg.analyse_as_type(env) for arg in self.args]
- if any(t is None for t in item_types):
- return None
- entry = env.declare_tuple_type(self.pos, item_types)
- return entry.type
- def coerce_to(self, dst_type, env):
- if self.type.is_ctuple:
- if dst_type.is_ctuple and self.type.size == dst_type.size:
- return self.coerce_to_ctuple(dst_type, env)
- elif dst_type is tuple_type or dst_type is py_object_type:
- coerced_args = [arg.coerce_to_pyobject(env) for arg in self.args]
- return TupleNode(self.pos, args=coerced_args, type=tuple_type, is_temp=1).analyse_types(env, skip_children=True)
- else:
- return self.coerce_to_pyobject(env).coerce_to(dst_type, env)
- elif dst_type.is_ctuple and not self.mult_factor:
- return self.coerce_to_ctuple(dst_type, env)
- else:
- return SequenceNode.coerce_to(self, dst_type, env)
- def as_list(self):
- t = ListNode(self.pos, args=self.args, mult_factor=self.mult_factor)
- if isinstance(self.constant_result, tuple):
- t.constant_result = list(self.constant_result)
- return t
- def is_simple(self):
- # either temp or constant => always simple
- return True
- def nonlocally_immutable(self):
- # either temp or constant => always safe
- return True
- def calculate_result_code(self):
- if len(self.args) > 0:
- return self.result_code
- else:
- return Naming.empty_tuple
- def calculate_constant_result(self):
- self.constant_result = tuple([
- arg.constant_result for arg in self.args])
- def compile_time_value(self, denv):
- values = self.compile_time_value_list(denv)
- try:
- return tuple(values)
- except Exception as e:
- self.compile_time_value_error(e)
- def generate_operation_code(self, code):
- if len(self.args) == 0:
- # result_code is Naming.empty_tuple
- return
- if self.is_literal or self.is_partly_literal:
- # The "mult_factor" is part of the deduplication if it is also constant, i.e. when
- # we deduplicate the multiplied result. Otherwise, only deduplicate the constant part.
- dedup_key = make_dedup_key(self.type, [self.mult_factor if self.is_literal else None] + self.args)
- tuple_target = code.get_py_const(py_object_type, 'tuple', cleanup_level=2, dedup_key=dedup_key)
- const_code = code.get_cached_constants_writer(tuple_target)
- if const_code is not None:
- # constant is not yet initialised
- const_code.mark_pos(self.pos)
- self.generate_sequence_packing_code(const_code, tuple_target, plain=not self.is_literal)
- const_code.put_giveref(tuple_target)
- if self.is_literal:
- self.result_code = tuple_target
- else:
- code.putln('%s = PyNumber_Multiply(%s, %s); %s' % (
- self.result(), tuple_target, self.mult_factor.py_result(),
- code.error_goto_if_null(self.result(), self.pos)
- ))
- code.put_gotref(self.py_result())
- else:
- self.type.entry.used = True
- self.generate_sequence_packing_code(code)
- class ListNode(SequenceNode):
- # List constructor.
- # obj_conversion_errors [PyrexError] used internally
- # orignial_args [ExprNode] used internally
- obj_conversion_errors = []
- type = list_type
- in_module_scope = False
- gil_message = "Constructing Python list"
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- # TODO: Infer non-object list arrays.
- return list_type
- def analyse_expressions(self, env):
- for arg in self.args:
- if arg.is_starred:
- arg.starred_expr_allowed_here = True
- node = SequenceNode.analyse_expressions(self, env)
- return node.coerce_to_pyobject(env)
- def analyse_types(self, env):
- with local_errors(ignore=True) as errors:
- self.original_args = list(self.args)
- node = SequenceNode.analyse_types(self, env)
- node.obj_conversion_errors = errors
- if env.is_module_scope:
- self.in_module_scope = True
- node = node._create_merge_node_if_necessary(env)
- return node
- def coerce_to(self, dst_type, env):
- if dst_type.is_pyobject:
- for err in self.obj_conversion_errors:
- report_error(err)
- self.obj_conversion_errors = []
- if not self.type.subtype_of(dst_type):
- error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
- elif (dst_type.is_array or dst_type.is_ptr) and dst_type.base_type is not PyrexTypes.c_void_type:
- array_length = len(self.args)
- if self.mult_factor:
- if isinstance(self.mult_factor.constant_result, _py_int_types):
- if self.mult_factor.constant_result <= 0:
- error(self.pos, "Cannot coerce non-positively multiplied list to '%s'" % dst_type)
- else:
- array_length *= self.mult_factor.constant_result
- else:
- error(self.pos, "Cannot coerce dynamically multiplied list to '%s'" % dst_type)
- base_type = dst_type.base_type
- self.type = PyrexTypes.CArrayType(base_type, array_length)
- for i in range(len(self.original_args)):
- arg = self.args[i]
- if isinstance(arg, CoerceToPyTypeNode):
- arg = arg.arg
- self.args[i] = arg.coerce_to(base_type, env)
- elif dst_type.is_cpp_class:
- # TODO(robertwb): Avoid object conversion for vector/list/set.
- return TypecastNode(self.pos, operand=self, type=PyrexTypes.py_object_type).coerce_to(dst_type, env)
- elif self.mult_factor:
- error(self.pos, "Cannot coerce multiplied list to '%s'" % dst_type)
- elif dst_type.is_struct:
- if len(self.args) > len(dst_type.scope.var_entries):
- error(self.pos, "Too many members for '%s'" % dst_type)
- else:
- if len(self.args) < len(dst_type.scope.var_entries):
- warning(self.pos, "Too few members for '%s'" % dst_type, 1)
- for i, (arg, member) in enumerate(zip(self.original_args, dst_type.scope.var_entries)):
- if isinstance(arg, CoerceToPyTypeNode):
- arg = arg.arg
- self.args[i] = arg.coerce_to(member.type, env)
- self.type = dst_type
- elif dst_type.is_ctuple:
- return self.coerce_to_ctuple(dst_type, env)
- else:
- self.type = error_type
- error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
- return self
- def as_list(self): # dummy for compatibility with TupleNode
- return self
- def as_tuple(self):
- t = TupleNode(self.pos, args=self.args, mult_factor=self.mult_factor)
- if isinstance(self.constant_result, list):
- t.constant_result = tuple(self.constant_result)
- return t
- def allocate_temp_result(self, code):
- if self.type.is_array:
- if self.in_module_scope:
- self.temp_code = code.funcstate.allocate_temp(
- self.type, manage_ref=False, static=True, reusable=False)
- else:
- # To be valid C++, we must allocate the memory on the stack
- # manually and be sure not to reuse it for something else.
- # Yes, this means that we leak a temp array variable.
- self.temp_code = code.funcstate.allocate_temp(
- self.type, manage_ref=False, reusable=False)
- else:
- SequenceNode.allocate_temp_result(self, code)
- def calculate_constant_result(self):
- if self.mult_factor:
- raise ValueError() # may exceed the compile time memory
- self.constant_result = [
- arg.constant_result for arg in self.args]
- def compile_time_value(self, denv):
- l = self.compile_time_value_list(denv)
- if self.mult_factor:
- l *= self.mult_factor.compile_time_value(denv)
- return l
- def generate_operation_code(self, code):
- if self.type.is_pyobject:
- for err in self.obj_conversion_errors:
- report_error(err)
- self.generate_sequence_packing_code(code)
- elif self.type.is_array:
- if self.mult_factor:
- code.putln("{")
- code.putln("Py_ssize_t %s;" % Naming.quick_temp_cname)
- code.putln("for ({i} = 0; {i} < {count}; {i}++) {{".format(
- i=Naming.quick_temp_cname, count=self.mult_factor.result()))
- offset = '+ (%d * %s)' % (len(self.args), Naming.quick_temp_cname)
- else:
- offset = ''
- for i, arg in enumerate(self.args):
- if arg.type.is_array:
- code.globalstate.use_utility_code(UtilityCode.load_cached("IncludeStringH", "StringTools.c"))
- code.putln("memcpy(&(%s[%s%s]), %s, sizeof(%s[0]));" % (
- self.result(), i, offset,
- arg.result(), self.result()
- ))
- else:
- code.putln("%s[%s%s] = %s;" % (
- self.result(),
- i,
- offset,
- arg.result()))
- if self.mult_factor:
- code.putln("}")
- code.putln("}")
- elif self.type.is_struct:
- for arg, member in zip(self.args, self.type.scope.var_entries):
- code.putln("%s.%s = %s;" % (
- self.result(),
- member.cname,
- arg.result()))
- else:
- raise InternalError("List type never specified")
- class ScopedExprNode(ExprNode):
- # Abstract base class for ExprNodes that have their own local
- # scope, such as generator expressions.
- #
- # expr_scope Scope the inner scope of the expression
- subexprs = []
- expr_scope = None
- # does this node really have a local scope, e.g. does it leak loop
- # variables or not? non-leaking Py3 behaviour is default, except
- # for list comprehensions where the behaviour differs in Py2 and
- # Py3 (set in Parsing.py based on parser context)
- has_local_scope = True
- def init_scope(self, outer_scope, expr_scope=None):
- if expr_scope is not None:
- self.expr_scope = expr_scope
- elif self.has_local_scope:
- self.expr_scope = Symtab.GeneratorExpressionScope(outer_scope)
- else:
- self.expr_scope = None
- def analyse_declarations(self, env):
- self.init_scope(env)
- def analyse_scoped_declarations(self, env):
- # this is called with the expr_scope as env
- pass
- def analyse_types(self, env):
- # no recursion here, the children will be analysed separately below
- return self
- def analyse_scoped_expressions(self, env):
- # this is called with the expr_scope as env
- return self
- def generate_evaluation_code(self, code):
- # set up local variables and free their references on exit
- generate_inner_evaluation_code = super(ScopedExprNode, self).generate_evaluation_code
- if not self.has_local_scope or not self.expr_scope.var_entries:
- # no local variables => delegate, done
- generate_inner_evaluation_code(code)
- return
- code.putln('{ /* enter inner scope */')
- py_entries = []
- for _, entry in sorted(item for item in self.expr_scope.entries.items() if item[0]):
- if not entry.in_closure:
- if entry.type.is_pyobject and entry.used:
- py_entries.append(entry)
- if not py_entries:
- # no local Python references => no cleanup required
- generate_inner_evaluation_code(code)
- code.putln('} /* exit inner scope */')
- return
- # must free all local Python references at each exit point
- old_loop_labels = code.new_loop_labels()
- old_error_label = code.new_error_label()
- generate_inner_evaluation_code(code)
- # normal (non-error) exit
- self._generate_vars_cleanup(code, py_entries)
- # error/loop body exit points
- exit_scope = code.new_label('exit_scope')
- code.put_goto(exit_scope)
- for label, old_label in ([(code.error_label, old_error_label)] +
- list(zip(code.get_loop_labels(), old_loop_labels))):
- if code.label_used(label):
- code.put_label(label)
- self._generate_vars_cleanup(code, py_entries)
- code.put_goto(old_label)
- code.put_label(exit_scope)
- code.putln('} /* exit inner scope */')
- code.set_loop_labels(old_loop_labels)
- code.error_label = old_error_label
- def _generate_vars_cleanup(self, code, py_entries):
- for entry in py_entries:
- if entry.is_cglobal:
- code.put_var_gotref(entry)
- code.put_decref_set(entry.cname, "Py_None")
- else:
- code.put_var_xdecref_clear(entry)
- class ComprehensionNode(ScopedExprNode):
- # A list/set/dict comprehension
- child_attrs = ["loop"]
- is_temp = True
- constant_result = not_a_constant
- def infer_type(self, env):
- return self.type
- def analyse_declarations(self, env):
- self.append.target = self # this is used in the PyList_Append of the inner loop
- self.init_scope(env)
- def analyse_scoped_declarations(self, env):
- self.loop.analyse_declarations(env)
- def analyse_types(self, env):
- if not self.has_local_scope:
- self.loop = self.loop.analyse_expressions(env)
- return self
- def analyse_scoped_expressions(self, env):
- if self.has_local_scope:
- self.loop = self.loop.analyse_expressions(env)
- return self
- def may_be_none(self):
- return False
- def generate_result_code(self, code):
- self.generate_operation_code(code)
- def generate_operation_code(self, code):
- if self.type is Builtin.list_type:
- create_code = 'PyList_New(0)'
- elif self.type is Builtin.set_type:
- create_code = 'PySet_New(NULL)'
- elif self.type is Builtin.dict_type:
- create_code = 'PyDict_New()'
- else:
- raise InternalError("illegal type for comprehension: %s" % self.type)
- code.putln('%s = %s; %s' % (
- self.result(), create_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- self.loop.generate_execution_code(code)
- def annotate(self, code):
- self.loop.annotate(code)
- class ComprehensionAppendNode(Node):
- # Need to be careful to avoid infinite recursion:
- # target must not be in child_attrs/subexprs
- child_attrs = ['expr']
- target = None
- type = PyrexTypes.c_int_type
- def analyse_expressions(self, env):
- self.expr = self.expr.analyse_expressions(env)
- if not self.expr.type.is_pyobject:
- self.expr = self.expr.coerce_to_pyobject(env)
- return self
- def generate_execution_code(self, code):
- if self.target.type is list_type:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ListCompAppend", "Optimize.c"))
- function = "__Pyx_ListComp_Append"
- elif self.target.type is set_type:
- function = "PySet_Add"
- else:
- raise InternalError(
- "Invalid type for comprehension node: %s" % self.target.type)
- self.expr.generate_evaluation_code(code)
- code.putln(code.error_goto_if("%s(%s, (PyObject*)%s)" % (
- function,
- self.target.result(),
- self.expr.result()
- ), self.pos))
- self.expr.generate_disposal_code(code)
- self.expr.free_temps(code)
- def generate_function_definitions(self, env, code):
- self.expr.generate_function_definitions(env, code)
- def annotate(self, code):
- self.expr.annotate(code)
- class DictComprehensionAppendNode(ComprehensionAppendNode):
- child_attrs = ['key_expr', 'value_expr']
- def analyse_expressions(self, env):
- self.key_expr = self.key_expr.analyse_expressions(env)
- if not self.key_expr.type.is_pyobject:
- self.key_expr = self.key_expr.coerce_to_pyobject(env)
- self.value_expr = self.value_expr.analyse_expressions(env)
- if not self.value_expr.type.is_pyobject:
- self.value_expr = self.value_expr.coerce_to_pyobject(env)
- return self
- def generate_execution_code(self, code):
- self.key_expr.generate_evaluation_code(code)
- self.value_expr.generate_evaluation_code(code)
- code.putln(code.error_goto_if("PyDict_SetItem(%s, (PyObject*)%s, (PyObject*)%s)" % (
- self.target.result(),
- self.key_expr.result(),
- self.value_expr.result()
- ), self.pos))
- self.key_expr.generate_disposal_code(code)
- self.key_expr.free_temps(code)
- self.value_expr.generate_disposal_code(code)
- self.value_expr.free_temps(code)
- def generate_function_definitions(self, env, code):
- self.key_expr.generate_function_definitions(env, code)
- self.value_expr.generate_function_definitions(env, code)
- def annotate(self, code):
- self.key_expr.annotate(code)
- self.value_expr.annotate(code)
- class InlinedGeneratorExpressionNode(ExprNode):
- # An inlined generator expression for which the result is calculated
- # inside of the loop and returned as a single, first and only Generator
- # return value.
- # This will only be created by transforms when replacing safe builtin
- # calls on generator expressions.
- #
- # gen GeneratorExpressionNode the generator, not containing any YieldExprNodes
- # orig_func String the name of the builtin function this node replaces
- # target ExprNode or None a 'target' for a ComprehensionAppend node
- subexprs = ["gen"]
- orig_func = None
- target = None
- is_temp = True
- type = py_object_type
- def __init__(self, pos, gen, comprehension_type=None, **kwargs):
- gbody = gen.def_node.gbody
- gbody.is_inlined = True
- if comprehension_type is not None:
- assert comprehension_type in (list_type, set_type, dict_type), comprehension_type
- gbody.inlined_comprehension_type = comprehension_type
- kwargs.update(
- target=RawCNameExprNode(pos, comprehension_type, Naming.retval_cname),
- type=comprehension_type,
- )
- super(InlinedGeneratorExpressionNode, self).__init__(pos, gen=gen, **kwargs)
- def may_be_none(self):
- return self.orig_func not in ('any', 'all', 'sorted')
- def infer_type(self, env):
- return self.type
- def analyse_types(self, env):
- self.gen = self.gen.analyse_expressions(env)
- return self
- def generate_result_code(self, code):
- code.putln("%s = __Pyx_Generator_Next(%s); %s" % (
- self.result(), self.gen.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- class MergedSequenceNode(ExprNode):
- """
- Merge a sequence of iterables into a set/list/tuple.
- The target collection is determined by self.type, which must be set externally.
- args [ExprNode]
- """
- subexprs = ['args']
- is_temp = True
- gil_message = "Constructing Python collection"
- def __init__(self, pos, args, type):
- if type in (list_type, tuple_type) and args and args[0].is_sequence_constructor:
- # construct a list directly from the first argument that we can then extend
- if args[0].type is not list_type:
- args[0] = ListNode(args[0].pos, args=args[0].args, is_temp=True)
- ExprNode.__init__(self, pos, args=args, type=type)
- def calculate_constant_result(self):
- result = []
- for item in self.args:
- if item.is_sequence_constructor and item.mult_factor:
- if item.mult_factor.constant_result <= 0:
- continue
- # otherwise, adding each item once should be enough
- if item.is_set_literal or item.is_sequence_constructor:
- # process items in order
- items = (arg.constant_result for arg in item.args)
- else:
- items = item.constant_result
- result.extend(items)
- if self.type is set_type:
- result = set(result)
- elif self.type is tuple_type:
- result = tuple(result)
- else:
- assert self.type is list_type
- self.constant_result = result
- def compile_time_value(self, denv):
- result = []
- for item in self.args:
- if item.is_sequence_constructor and item.mult_factor:
- if item.mult_factor.compile_time_value(denv) <= 0:
- continue
- if item.is_set_literal or item.is_sequence_constructor:
- # process items in order
- items = (arg.compile_time_value(denv) for arg in item.args)
- else:
- items = item.compile_time_value(denv)
- result.extend(items)
- if self.type is set_type:
- try:
- result = set(result)
- except Exception as e:
- self.compile_time_value_error(e)
- elif self.type is tuple_type:
- result = tuple(result)
- else:
- assert self.type is list_type
- return result
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- return self.type
- def analyse_types(self, env):
- args = [
- arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node(
- # FIXME: CPython's error message starts with the runtime function name
- 'argument after * must be an iterable, not NoneType')
- for arg in self.args
- ]
- if len(args) == 1 and args[0].type is self.type:
- # strip this intermediate node and use the bare collection
- return args[0]
- assert self.type in (set_type, list_type, tuple_type)
- self.args = args
- return self
- def may_be_none(self):
- return False
- def generate_evaluation_code(self, code):
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- is_set = self.type is set_type
- args = iter(self.args)
- item = next(args)
- item.generate_evaluation_code(code)
- if (is_set and item.is_set_literal or
- not is_set and item.is_sequence_constructor and item.type is list_type):
- code.putln("%s = %s;" % (self.result(), item.py_result()))
- item.generate_post_assignment_code(code)
- else:
- code.putln("%s = %s(%s); %s" % (
- self.result(),
- 'PySet_New' if is_set else 'PySequence_List',
- item.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- item.generate_disposal_code(code)
- item.free_temps(code)
- helpers = set()
- if is_set:
- add_func = "PySet_Add"
- extend_func = "__Pyx_PySet_Update"
- else:
- add_func = "__Pyx_ListComp_Append"
- extend_func = "__Pyx_PyList_Extend"
- for item in args:
- if (is_set and (item.is_set_literal or item.is_sequence_constructor) or
- (item.is_sequence_constructor and not item.mult_factor)):
- if not is_set and item.args:
- helpers.add(("ListCompAppend", "Optimize.c"))
- for arg in item.args:
- arg.generate_evaluation_code(code)
- code.put_error_if_neg(arg.pos, "%s(%s, %s)" % (
- add_func,
- self.result(),
- arg.py_result()))
- arg.generate_disposal_code(code)
- arg.free_temps(code)
- continue
- if is_set:
- helpers.add(("PySet_Update", "Builtins.c"))
- else:
- helpers.add(("ListExtend", "Optimize.c"))
- item.generate_evaluation_code(code)
- code.put_error_if_neg(item.pos, "%s(%s, %s)" % (
- extend_func,
- self.result(),
- item.py_result()))
- item.generate_disposal_code(code)
- item.free_temps(code)
- if self.type is tuple_type:
- code.putln("{")
- code.putln("PyObject *%s = PyList_AsTuple(%s);" % (
- Naming.quick_temp_cname,
- self.result()))
- code.put_decref(self.result(), py_object_type)
- code.putln("%s = %s; %s" % (
- self.result(),
- Naming.quick_temp_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- code.putln("}")
- for helper in sorted(helpers):
- code.globalstate.use_utility_code(UtilityCode.load_cached(*helper))
- def annotate(self, code):
- for item in self.args:
- item.annotate(code)
- class SetNode(ExprNode):
- """
- Set constructor.
- """
- subexprs = ['args']
- type = set_type
- is_set_literal = True
- gil_message = "Constructing Python set"
- def analyse_types(self, env):
- for i in range(len(self.args)):
- arg = self.args[i]
- arg = arg.analyse_types(env)
- self.args[i] = arg.coerce_to_pyobject(env)
- self.type = set_type
- self.is_temp = 1
- return self
- def may_be_none(self):
- return False
- def calculate_constant_result(self):
- self.constant_result = set([arg.constant_result for arg in self.args])
- def compile_time_value(self, denv):
- values = [arg.compile_time_value(denv) for arg in self.args]
- try:
- return set(values)
- except Exception as e:
- self.compile_time_value_error(e)
- def generate_evaluation_code(self, code):
- for arg in self.args:
- arg.generate_evaluation_code(code)
- self.allocate_temp_result(code)
- code.putln(
- "%s = PySet_New(0); %s" % (
- self.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- for arg in self.args:
- code.put_error_if_neg(
- self.pos,
- "PySet_Add(%s, %s)" % (self.result(), arg.py_result()))
- arg.generate_disposal_code(code)
- arg.free_temps(code)
- class DictNode(ExprNode):
- # Dictionary constructor.
- #
- # key_value_pairs [DictItemNode]
- # exclude_null_values [boolean] Do not add NULL values to dict
- #
- # obj_conversion_errors [PyrexError] used internally
- subexprs = ['key_value_pairs']
- is_temp = 1
- exclude_null_values = False
- type = dict_type
- is_dict_literal = True
- reject_duplicates = False
- obj_conversion_errors = []
- @classmethod
- def from_pairs(cls, pos, pairs):
- return cls(pos, key_value_pairs=[
- DictItemNode(pos, key=k, value=v) for k, v in pairs])
- def calculate_constant_result(self):
- self.constant_result = dict([
- item.constant_result for item in self.key_value_pairs])
- def compile_time_value(self, denv):
- pairs = [(item.key.compile_time_value(denv), item.value.compile_time_value(denv))
- for item in self.key_value_pairs]
- try:
- return dict(pairs)
- except Exception as e:
- self.compile_time_value_error(e)
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- # TODO: Infer struct constructors.
- return dict_type
- def analyse_types(self, env):
- with local_errors(ignore=True) as errors:
- self.key_value_pairs = [
- item.analyse_types(env)
- for item in self.key_value_pairs
- ]
- self.obj_conversion_errors = errors
- return self
- def may_be_none(self):
- return False
- def coerce_to(self, dst_type, env):
- if dst_type.is_pyobject:
- self.release_errors()
- if self.type.is_struct_or_union:
- if not dict_type.subtype_of(dst_type):
- error(self.pos, "Cannot interpret struct as non-dict type '%s'" % dst_type)
- return DictNode(self.pos, key_value_pairs=[
- DictItemNode(item.pos, key=item.key.coerce_to_pyobject(env),
- value=item.value.coerce_to_pyobject(env))
- for item in self.key_value_pairs])
- if not self.type.subtype_of(dst_type):
- error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
- elif dst_type.is_struct_or_union:
- self.type = dst_type
- if not dst_type.is_struct and len(self.key_value_pairs) != 1:
- error(self.pos, "Exactly one field must be specified to convert to union '%s'" % dst_type)
- elif dst_type.is_struct and len(self.key_value_pairs) < len(dst_type.scope.var_entries):
- warning(self.pos, "Not all members given for struct '%s'" % dst_type, 1)
- for item in self.key_value_pairs:
- if isinstance(item.key, CoerceToPyTypeNode):
- item.key = item.key.arg
- if not item.key.is_string_literal:
- error(item.key.pos, "Invalid struct field identifier")
- item.key = StringNode(item.key.pos, value="<error>")
- else:
- key = str(item.key.value) # converts string literals to unicode in Py3
- member = dst_type.scope.lookup_here(key)
- if not member:
- error(item.key.pos, "struct '%s' has no field '%s'" % (dst_type, key))
- else:
- value = item.value
- if isinstance(value, CoerceToPyTypeNode):
- value = value.arg
- item.value = value.coerce_to(member.type, env)
- else:
- self.type = error_type
- error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
- return self
- def release_errors(self):
- for err in self.obj_conversion_errors:
- report_error(err)
- self.obj_conversion_errors = []
- gil_message = "Constructing Python dict"
- def generate_evaluation_code(self, code):
- # Custom method used here because key-value
- # pairs are evaluated and used one at a time.
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- is_dict = self.type.is_pyobject
- if is_dict:
- self.release_errors()
- code.putln(
- "%s = __Pyx_PyDict_NewPresized(%d); %s" % (
- self.result(),
- len(self.key_value_pairs),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- keys_seen = set()
- key_type = None
- needs_error_helper = False
- for item in self.key_value_pairs:
- item.generate_evaluation_code(code)
- if is_dict:
- if self.exclude_null_values:
- code.putln('if (%s) {' % item.value.py_result())
- key = item.key
- if self.reject_duplicates:
- if keys_seen is not None:
- # avoid runtime 'in' checks for literals that we can do at compile time
- if not key.is_string_literal:
- keys_seen = None
- elif key.value in keys_seen:
- # FIXME: this could be a compile time error, at least in Cython code
- keys_seen = None
- elif key_type is not type(key.value):
- if key_type is None:
- key_type = type(key.value)
- keys_seen.add(key.value)
- else:
- # different types => may not be able to compare at compile time
- keys_seen = None
- else:
- keys_seen.add(key.value)
- if keys_seen is None:
- code.putln('if (unlikely(PyDict_Contains(%s, %s))) {' % (
- self.result(), key.py_result()))
- # currently only used in function calls
- needs_error_helper = True
- code.putln('__Pyx_RaiseDoubleKeywordsError("function", %s); %s' % (
- key.py_result(),
- code.error_goto(item.pos)))
- code.putln("} else {")
- code.put_error_if_neg(self.pos, "PyDict_SetItem(%s, %s, %s)" % (
- self.result(),
- item.key.py_result(),
- item.value.py_result()))
- if self.reject_duplicates and keys_seen is None:
- code.putln('}')
- if self.exclude_null_values:
- code.putln('}')
- else:
- code.putln("%s.%s = %s;" % (
- self.result(),
- item.key.value,
- item.value.result()))
- item.generate_disposal_code(code)
- item.free_temps(code)
- if needs_error_helper:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("RaiseDoubleKeywords", "FunctionArguments.c"))
- def annotate(self, code):
- for item in self.key_value_pairs:
- item.annotate(code)
- class DictItemNode(ExprNode):
- # Represents a single item in a DictNode
- #
- # key ExprNode
- # value ExprNode
- subexprs = ['key', 'value']
- nogil_check = None # Parent DictNode takes care of it
- def calculate_constant_result(self):
- self.constant_result = (
- self.key.constant_result, self.value.constant_result)
- def analyse_types(self, env):
- self.key = self.key.analyse_types(env)
- self.value = self.value.analyse_types(env)
- self.key = self.key.coerce_to_pyobject(env)
- self.value = self.value.coerce_to_pyobject(env)
- return self
- def generate_evaluation_code(self, code):
- self.key.generate_evaluation_code(code)
- self.value.generate_evaluation_code(code)
- def generate_disposal_code(self, code):
- self.key.generate_disposal_code(code)
- self.value.generate_disposal_code(code)
- def free_temps(self, code):
- self.key.free_temps(code)
- self.value.free_temps(code)
- def __iter__(self):
- return iter([self.key, self.value])
- class SortedDictKeysNode(ExprNode):
- # build sorted list of dict keys, e.g. for dir()
- subexprs = ['arg']
- is_temp = True
- def __init__(self, arg):
- ExprNode.__init__(self, arg.pos, arg=arg)
- self.type = Builtin.list_type
- def analyse_types(self, env):
- arg = self.arg.analyse_types(env)
- if arg.type is Builtin.dict_type:
- arg = arg.as_none_safe_node(
- "'NoneType' object is not iterable")
- self.arg = arg
- return self
- def may_be_none(self):
- return False
- def generate_result_code(self, code):
- dict_result = self.arg.py_result()
- if self.arg.type is Builtin.dict_type:
- code.putln('%s = PyDict_Keys(%s); %s' % (
- self.result(), dict_result,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- else:
- # originally used PyMapping_Keys() here, but that may return a tuple
- code.globalstate.use_utility_code(UtilityCode.load_cached(
- 'PyObjectCallMethod0', 'ObjectHandling.c'))
- keys_cname = code.intern_identifier(StringEncoding.EncodedString("keys"))
- code.putln('%s = __Pyx_PyObject_CallMethod0(%s, %s); %s' % (
- self.result(), dict_result, keys_cname,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- code.putln("if (unlikely(!PyList_Check(%s))) {" % self.result())
- code.put_decref_set(self.result(), "PySequence_List(%s)" % self.result())
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.py_result())
- code.putln("}")
- code.put_error_if_neg(
- self.pos, 'PyList_Sort(%s)' % self.py_result())
- class ModuleNameMixin(object):
- def get_py_mod_name(self, code):
- return code.get_py_string_const(
- self.module_name, identifier=True)
- def get_py_qualified_name(self, code):
- return code.get_py_string_const(
- self.qualname, identifier=True)
- class ClassNode(ExprNode, ModuleNameMixin):
- # Helper class used in the implementation of Python
- # class definitions. Constructs a class object given
- # a name, tuple of bases and class dictionary.
- #
- # name EncodedString Name of the class
- # class_def_node PyClassDefNode PyClassDefNode defining this class
- # doc ExprNode or None Doc string
- # module_name EncodedString Name of defining module
- subexprs = ['doc']
- type = py_object_type
- is_temp = True
- def infer_type(self, env):
- # TODO: could return 'type' in some cases
- return py_object_type
- def analyse_types(self, env):
- if self.doc:
- self.doc = self.doc.analyse_types(env)
- self.doc = self.doc.coerce_to_pyobject(env)
- env.use_utility_code(UtilityCode.load_cached("CreateClass", "ObjectHandling.c"))
- return self
- def may_be_none(self):
- return True
- gil_message = "Constructing Python class"
- def generate_result_code(self, code):
- class_def_node = self.class_def_node
- cname = code.intern_identifier(self.name)
- if self.doc:
- code.put_error_if_neg(self.pos,
- 'PyDict_SetItem(%s, %s, %s)' % (
- class_def_node.dict.py_result(),
- code.intern_identifier(
- StringEncoding.EncodedString("__doc__")),
- self.doc.py_result()))
- py_mod_name = self.get_py_mod_name(code)
- qualname = self.get_py_qualified_name(code)
- code.putln(
- '%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % (
- self.result(),
- class_def_node.bases.py_result(),
- class_def_node.dict.py_result(),
- cname,
- qualname,
- py_mod_name,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class Py3ClassNode(ExprNode):
- # Helper class used in the implementation of Python3+
- # class definitions. Constructs a class object given
- # a name, tuple of bases and class dictionary.
- #
- # name EncodedString Name of the class
- # module_name EncodedString Name of defining module
- # class_def_node PyClassDefNode PyClassDefNode defining this class
- # calculate_metaclass bool should call CalculateMetaclass()
- # allow_py2_metaclass bool should look for Py2 metaclass
- subexprs = []
- type = py_object_type
- is_temp = True
- def infer_type(self, env):
- # TODO: could return 'type' in some cases
- return py_object_type
- def analyse_types(self, env):
- return self
- def may_be_none(self):
- return True
- gil_message = "Constructing Python class"
- def generate_result_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("Py3ClassCreate", "ObjectHandling.c"))
- cname = code.intern_identifier(self.name)
- class_def_node = self.class_def_node
- mkw = class_def_node.mkw.py_result() if class_def_node.mkw else 'NULL'
- if class_def_node.metaclass:
- metaclass = class_def_node.metaclass.py_result()
- else:
- metaclass = "((PyObject*)&__Pyx_DefaultClassType)"
- code.putln(
- '%s = __Pyx_Py3ClassCreate(%s, %s, %s, %s, %s, %d, %d); %s' % (
- self.result(),
- metaclass,
- cname,
- class_def_node.bases.py_result(),
- class_def_node.dict.py_result(),
- mkw,
- self.calculate_metaclass,
- self.allow_py2_metaclass,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class PyClassMetaclassNode(ExprNode):
- # Helper class holds Python3 metaclass object
- #
- # class_def_node PyClassDefNode PyClassDefNode defining this class
- subexprs = []
- def analyse_types(self, env):
- self.type = py_object_type
- self.is_temp = True
- return self
- def may_be_none(self):
- return True
- def generate_result_code(self, code):
- bases = self.class_def_node.bases
- mkw = self.class_def_node.mkw
- if mkw:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("Py3MetaclassGet", "ObjectHandling.c"))
- call = "__Pyx_Py3MetaclassGet(%s, %s)" % (
- bases.result(),
- mkw.result())
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CalculateMetaclass", "ObjectHandling.c"))
- call = "__Pyx_CalculateMetaclass(NULL, %s)" % (
- bases.result())
- code.putln(
- "%s = %s; %s" % (
- self.result(), call,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
- # Helper class holds Python3 namespace object
- #
- # All this are not owned by this node
- # class_def_node PyClassDefNode PyClassDefNode defining this class
- # doc ExprNode or None Doc string (owned)
- subexprs = ['doc']
- def analyse_types(self, env):
- if self.doc:
- self.doc = self.doc.analyse_types(env).coerce_to_pyobject(env)
- self.type = py_object_type
- self.is_temp = 1
- return self
- def may_be_none(self):
- return True
- def generate_result_code(self, code):
- cname = code.intern_identifier(self.name)
- py_mod_name = self.get_py_mod_name(code)
- qualname = self.get_py_qualified_name(code)
- class_def_node = self.class_def_node
- null = "(PyObject *) NULL"
- doc_code = self.doc.result() if self.doc else null
- mkw = class_def_node.mkw.py_result() if class_def_node.mkw else null
- metaclass = class_def_node.metaclass.py_result() if class_def_node.metaclass else null
- code.putln(
- "%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % (
- self.result(),
- metaclass,
- class_def_node.bases.result(),
- cname,
- qualname,
- mkw,
- py_mod_name,
- doc_code,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class ClassCellInjectorNode(ExprNode):
- # Initialize CyFunction.func_classobj
- is_temp = True
- type = py_object_type
- subexprs = []
- is_active = False
- def analyse_expressions(self, env):
- return self
- def generate_result_code(self, code):
- assert self.is_active
- code.putln(
- '%s = PyList_New(0); %s' % (
- self.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- def generate_injection_code(self, code, classobj_cname):
- assert self.is_active
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CyFunctionClassCell", "CythonFunction.c"))
- code.put_error_if_neg(self.pos, '__Pyx_CyFunction_InitClassCell(%s, %s)' % (
- self.result(), classobj_cname))
- class ClassCellNode(ExprNode):
- # Class Cell for noargs super()
- subexprs = []
- is_temp = True
- is_generator = False
- type = py_object_type
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- if not self.is_generator:
- code.putln('%s = __Pyx_CyFunction_GetClassObj(%s);' % (
- self.result(),
- Naming.self_cname))
- else:
- code.putln('%s = %s->classobj;' % (
- self.result(), Naming.generator_cname))
- code.putln(
- 'if (!%s) { PyErr_SetString(PyExc_SystemError, '
- '"super(): empty __class__ cell"); %s }' % (
- self.result(),
- code.error_goto(self.pos)))
- code.put_incref(self.result(), py_object_type)
- class PyCFunctionNode(ExprNode, ModuleNameMixin):
- # Helper class used in the implementation of Python
- # functions. Constructs a PyCFunction object
- # from a PyMethodDef struct.
- #
- # pymethdef_cname string PyMethodDef structure
- # self_object ExprNode or None
- # binding bool
- # def_node DefNode the Python function node
- # module_name EncodedString Name of defining module
- # code_object CodeObjectNode the PyCodeObject creator node
- subexprs = ['code_object', 'defaults_tuple', 'defaults_kwdict',
- 'annotations_dict']
- self_object = None
- code_object = None
- binding = False
- def_node = None
- defaults = None
- defaults_struct = None
- defaults_pyobjects = 0
- defaults_tuple = None
- defaults_kwdict = None
- annotations_dict = None
- type = py_object_type
- is_temp = 1
- specialized_cpdefs = None
- is_specialization = False
- @classmethod
- def from_defnode(cls, node, binding):
- return cls(node.pos,
- def_node=node,
- pymethdef_cname=node.entry.pymethdef_cname,
- binding=binding or node.specialized_cpdefs,
- specialized_cpdefs=node.specialized_cpdefs,
- code_object=CodeObjectNode(node))
- def analyse_types(self, env):
- if self.binding:
- self.analyse_default_args(env)
- return self
- def analyse_default_args(self, env):
- """
- Handle non-literal function's default arguments.
- """
- nonliteral_objects = []
- nonliteral_other = []
- default_args = []
- default_kwargs = []
- annotations = []
- # For global cpdef functions and def/cpdef methods in cdef classes, we must use global constants
- # for default arguments to avoid the dependency on the CyFunction object as 'self' argument
- # in the underlying C function. Basically, cpdef functions/methods are static C functions,
- # so their optional arguments must be static, too.
- # TODO: change CyFunction implementation to pass both function object and owning object for method calls
- must_use_constants = env.is_c_class_scope or (self.def_node.is_wrapper and env.is_module_scope)
- for arg in self.def_node.args:
- if arg.default and not must_use_constants:
- if not arg.default.is_literal:
- arg.is_dynamic = True
- if arg.type.is_pyobject:
- nonliteral_objects.append(arg)
- else:
- nonliteral_other.append(arg)
- else:
- arg.default = DefaultLiteralArgNode(arg.pos, arg.default)
- if arg.kw_only:
- default_kwargs.append(arg)
- else:
- default_args.append(arg)
- if arg.annotation:
- arg.annotation = self.analyse_annotation(env, arg.annotation)
- annotations.append((arg.pos, arg.name, arg.annotation))
- for arg in (self.def_node.star_arg, self.def_node.starstar_arg):
- if arg and arg.annotation:
- arg.annotation = self.analyse_annotation(env, arg.annotation)
- annotations.append((arg.pos, arg.name, arg.annotation))
- annotation = self.def_node.return_type_annotation
- if annotation:
- annotation = self.analyse_annotation(env, annotation)
- self.def_node.return_type_annotation = annotation
- annotations.append((annotation.pos, StringEncoding.EncodedString("return"), annotation))
- if nonliteral_objects or nonliteral_other:
- module_scope = env.global_scope()
- cname = module_scope.next_id(Naming.defaults_struct_prefix)
- scope = Symtab.StructOrUnionScope(cname)
- self.defaults = []
- for arg in nonliteral_objects:
- entry = scope.declare_var(arg.name, arg.type, None,
- Naming.arg_prefix + arg.name,
- allow_pyobject=True)
- self.defaults.append((arg, entry))
- for arg in nonliteral_other:
- entry = scope.declare_var(arg.name, arg.type, None,
- Naming.arg_prefix + arg.name,
- allow_pyobject=False, allow_memoryview=True)
- self.defaults.append((arg, entry))
- entry = module_scope.declare_struct_or_union(
- None, 'struct', scope, 1, None, cname=cname)
- self.defaults_struct = scope
- self.defaults_pyobjects = len(nonliteral_objects)
- for arg, entry in self.defaults:
- arg.default_value = '%s->%s' % (
- Naming.dynamic_args_cname, entry.cname)
- self.def_node.defaults_struct = self.defaults_struct.name
- if default_args or default_kwargs:
- if self.defaults_struct is None:
- if default_args:
- defaults_tuple = TupleNode(self.pos, args=[
- arg.default for arg in default_args])
- self.defaults_tuple = defaults_tuple.analyse_types(env).coerce_to_pyobject(env)
- if default_kwargs:
- defaults_kwdict = DictNode(self.pos, key_value_pairs=[
- DictItemNode(
- arg.pos,
- key=IdentifierStringNode(arg.pos, value=arg.name),
- value=arg.default)
- for arg in default_kwargs])
- self.defaults_kwdict = defaults_kwdict.analyse_types(env)
- else:
- if default_args:
- defaults_tuple = DefaultsTupleNode(
- self.pos, default_args, self.defaults_struct)
- else:
- defaults_tuple = NoneNode(self.pos)
- if default_kwargs:
- defaults_kwdict = DefaultsKwDictNode(
- self.pos, default_kwargs, self.defaults_struct)
- else:
- defaults_kwdict = NoneNode(self.pos)
- defaults_getter = Nodes.DefNode(
- self.pos, args=[], star_arg=None, starstar_arg=None,
- body=Nodes.ReturnStatNode(
- self.pos, return_type=py_object_type,
- value=TupleNode(
- self.pos, args=[defaults_tuple, defaults_kwdict])),
- decorators=None,
- name=StringEncoding.EncodedString("__defaults__"))
- # defaults getter must never live in class scopes, it's always a module function
- module_scope = env.global_scope()
- defaults_getter.analyse_declarations(module_scope)
- defaults_getter = defaults_getter.analyse_expressions(module_scope)
- defaults_getter.body = defaults_getter.body.analyse_expressions(
- defaults_getter.local_scope)
- defaults_getter.py_wrapper_required = False
- defaults_getter.pymethdef_required = False
- self.def_node.defaults_getter = defaults_getter
- if annotations:
- annotations_dict = DictNode(self.pos, key_value_pairs=[
- DictItemNode(
- pos, key=IdentifierStringNode(pos, value=name),
- value=value)
- for pos, name, value in annotations])
- self.annotations_dict = annotations_dict.analyse_types(env)
- def analyse_annotation(self, env, annotation):
- if annotation is None:
- return None
- atype = annotation.analyse_as_type(env)
- if atype is not None:
- # Keep parsed types as strings as they might not be Python representable.
- annotation = UnicodeNode(
- annotation.pos,
- value=StringEncoding.EncodedString(atype.declaration_code('', for_display=True)))
- annotation = annotation.analyse_types(env)
- if not annotation.type.is_pyobject:
- annotation = annotation.coerce_to_pyobject(env)
- return annotation
- def may_be_none(self):
- return False
- gil_message = "Constructing Python function"
- def self_result_code(self):
- if self.self_object is None:
- self_result = "NULL"
- else:
- self_result = self.self_object.py_result()
- return self_result
- def generate_result_code(self, code):
- if self.binding:
- self.generate_cyfunction_code(code)
- else:
- self.generate_pycfunction_code(code)
- def generate_pycfunction_code(self, code):
- py_mod_name = self.get_py_mod_name(code)
- code.putln(
- '%s = PyCFunction_NewEx(&%s, %s, %s); %s' % (
- self.result(),
- self.pymethdef_cname,
- self.self_result_code(),
- py_mod_name,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- def generate_cyfunction_code(self, code):
- if self.specialized_cpdefs:
- def_node = self.specialized_cpdefs[0]
- else:
- def_node = self.def_node
- if self.specialized_cpdefs or self.is_specialization:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("FusedFunction", "CythonFunction.c"))
- constructor = "__pyx_FusedFunction_New"
- else:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CythonFunction", "CythonFunction.c"))
- constructor = "__Pyx_CyFunction_New"
- if self.code_object:
- code_object_result = self.code_object.py_result()
- else:
- code_object_result = 'NULL'
- flags = []
- if def_node.is_staticmethod:
- flags.append('__Pyx_CYFUNCTION_STATICMETHOD')
- elif def_node.is_classmethod:
- flags.append('__Pyx_CYFUNCTION_CLASSMETHOD')
- if def_node.local_scope.parent_scope.is_c_class_scope and not def_node.entry.is_anonymous:
- flags.append('__Pyx_CYFUNCTION_CCLASS')
- if flags:
- flags = ' | '.join(flags)
- else:
- flags = '0'
- code.putln(
- '%s = %s(&%s, %s, %s, %s, %s, %s, %s); %s' % (
- self.result(),
- constructor,
- self.pymethdef_cname,
- flags,
- self.get_py_qualified_name(code),
- self.self_result_code(),
- self.get_py_mod_name(code),
- Naming.moddict_cname,
- code_object_result,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- if def_node.requires_classobj:
- assert code.pyclass_stack, "pyclass_stack is empty"
- class_node = code.pyclass_stack[-1]
- code.put_incref(self.py_result(), py_object_type)
- code.putln(
- 'PyList_Append(%s, %s);' % (
- class_node.class_cell.result(),
- self.result()))
- code.put_giveref(self.py_result())
- if self.defaults:
- code.putln(
- 'if (!__Pyx_CyFunction_InitDefaults(%s, sizeof(%s), %d)) %s' % (
- self.result(), self.defaults_struct.name,
- self.defaults_pyobjects, code.error_goto(self.pos)))
- defaults = '__Pyx_CyFunction_Defaults(%s, %s)' % (
- self.defaults_struct.name, self.result())
- for arg, entry in self.defaults:
- arg.generate_assignment_code(code, target='%s->%s' % (
- defaults, entry.cname))
- if self.defaults_tuple:
- code.putln('__Pyx_CyFunction_SetDefaultsTuple(%s, %s);' % (
- self.result(), self.defaults_tuple.py_result()))
- if self.defaults_kwdict:
- code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % (
- self.result(), self.defaults_kwdict.py_result()))
- if def_node.defaults_getter and not self.specialized_cpdefs:
- # Fused functions do not support dynamic defaults, only their specialisations can have them for now.
- code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % (
- self.result(), def_node.defaults_getter.entry.pyfunc_cname))
- if self.annotations_dict:
- code.putln('__Pyx_CyFunction_SetAnnotationsDict(%s, %s);' % (
- self.result(), self.annotations_dict.py_result()))
- class InnerFunctionNode(PyCFunctionNode):
- # Special PyCFunctionNode that depends on a closure class
- #
- binding = True
- needs_self_code = True
- def self_result_code(self):
- if self.needs_self_code:
- return "((PyObject*)%s)" % Naming.cur_scope_cname
- return "NULL"
- class CodeObjectNode(ExprNode):
- # Create a PyCodeObject for a CyFunction instance.
- #
- # def_node DefNode the Python function node
- # varnames TupleNode a tuple with all local variable names
- subexprs = ['varnames']
- is_temp = False
- result_code = None
- def __init__(self, def_node):
- ExprNode.__init__(self, def_node.pos, def_node=def_node)
- args = list(def_node.args)
- # if we have args/kwargs, then the first two in var_entries are those
- local_vars = [arg for arg in def_node.local_scope.var_entries if arg.name]
- self.varnames = TupleNode(
- def_node.pos,
- args=[IdentifierStringNode(arg.pos, value=arg.name)
- for arg in args + local_vars],
- is_temp=0,
- is_literal=1)
- def may_be_none(self):
- return False
- def calculate_result_code(self, code=None):
- if self.result_code is None:
- self.result_code = code.get_py_const(py_object_type, 'codeobj', cleanup_level=2)
- return self.result_code
- def generate_result_code(self, code):
- if self.result_code is None:
- self.result_code = code.get_py_const(py_object_type, 'codeobj', cleanup_level=2)
- code = code.get_cached_constants_writer(self.result_code)
- if code is None:
- return # already initialised
- code.mark_pos(self.pos)
- func = self.def_node
- func_name = code.get_py_string_const(
- func.name, identifier=True, is_str=False, unicode_value=func.name)
- # FIXME: better way to get the module file path at module init time? Encoding to use?
- file_path = StringEncoding.bytes_literal(func.pos[0].get_filenametable_entry().encode('utf8'), 'utf8')
- # XXX Use get_description() to set arcadia root relative filename
- file_path = StringEncoding.bytes_literal(func.pos[0].get_description().encode('utf8'), 'utf8')
- file_path_const = code.get_py_string_const(file_path, identifier=False, is_str=True)
- # This combination makes CPython create a new dict for "frame.f_locals" (see GH #1836).
- flags = ['CO_OPTIMIZED', 'CO_NEWLOCALS']
- if self.def_node.star_arg:
- flags.append('CO_VARARGS')
- if self.def_node.starstar_arg:
- flags.append('CO_VARKEYWORDS')
- code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % (
- self.result_code,
- len(func.args) - func.num_kwonly_args, # argcount
- func.num_kwonly_args, # kwonlyargcount (Py3 only)
- len(self.varnames.args), # nlocals
- '|'.join(flags) or '0', # flags
- Naming.empty_bytes, # code
- Naming.empty_tuple, # consts
- Naming.empty_tuple, # names (FIXME)
- self.varnames.result(), # varnames
- Naming.empty_tuple, # freevars (FIXME)
- Naming.empty_tuple, # cellvars (FIXME)
- file_path_const, # filename
- func_name, # name
- self.pos[1], # firstlineno
- Naming.empty_bytes, # lnotab
- code.error_goto_if_null(self.result_code, self.pos),
- ))
- class DefaultLiteralArgNode(ExprNode):
- # CyFunction's literal argument default value
- #
- # Evaluate literal only once.
- subexprs = []
- is_literal = True
- is_temp = False
- def __init__(self, pos, arg):
- super(DefaultLiteralArgNode, self).__init__(pos)
- self.arg = arg
- self.type = self.arg.type
- self.evaluated = False
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- pass
- def generate_evaluation_code(self, code):
- if not self.evaluated:
- self.arg.generate_evaluation_code(code)
- self.evaluated = True
- def result(self):
- return self.type.cast_code(self.arg.result())
- class DefaultNonLiteralArgNode(ExprNode):
- # CyFunction's non-literal argument default value
- subexprs = []
- def __init__(self, pos, arg, defaults_struct):
- super(DefaultNonLiteralArgNode, self).__init__(pos)
- self.arg = arg
- self.defaults_struct = defaults_struct
- def analyse_types(self, env):
- self.type = self.arg.type
- self.is_temp = False
- return self
- def generate_result_code(self, code):
- pass
- def result(self):
- return '__Pyx_CyFunction_Defaults(%s, %s)->%s' % (
- self.defaults_struct.name, Naming.self_cname,
- self.defaults_struct.lookup(self.arg.name).cname)
- class DefaultsTupleNode(TupleNode):
- # CyFunction's __defaults__ tuple
- def __init__(self, pos, defaults, defaults_struct):
- args = []
- for arg in defaults:
- if not arg.default.is_literal:
- arg = DefaultNonLiteralArgNode(pos, arg, defaults_struct)
- else:
- arg = arg.default
- args.append(arg)
- super(DefaultsTupleNode, self).__init__(pos, args=args)
- def analyse_types(self, env, skip_children=False):
- return super(DefaultsTupleNode, self).analyse_types(env, skip_children).coerce_to_pyobject(env)
- class DefaultsKwDictNode(DictNode):
- # CyFunction's __kwdefaults__ dict
- def __init__(self, pos, defaults, defaults_struct):
- items = []
- for arg in defaults:
- name = IdentifierStringNode(arg.pos, value=arg.name)
- if not arg.default.is_literal:
- arg = DefaultNonLiteralArgNode(pos, arg, defaults_struct)
- else:
- arg = arg.default
- items.append(DictItemNode(arg.pos, key=name, value=arg))
- super(DefaultsKwDictNode, self).__init__(pos, key_value_pairs=items)
- class LambdaNode(InnerFunctionNode):
- # Lambda expression node (only used as a function reference)
- #
- # args [CArgDeclNode] formal arguments
- # star_arg PyArgDeclNode or None * argument
- # starstar_arg PyArgDeclNode or None ** argument
- # lambda_name string a module-globally unique lambda name
- # result_expr ExprNode
- # def_node DefNode the underlying function 'def' node
- child_attrs = ['def_node']
- name = StringEncoding.EncodedString('<lambda>')
- def analyse_declarations(self, env):
- self.lambda_name = self.def_node.lambda_name = env.next_id('lambda')
- self.def_node.no_assignment_synthesis = True
- self.def_node.pymethdef_required = True
- self.def_node.analyse_declarations(env)
- self.def_node.is_cyfunction = True
- self.pymethdef_cname = self.def_node.entry.pymethdef_cname
- env.add_lambda_def(self.def_node)
- def analyse_types(self, env):
- self.def_node = self.def_node.analyse_expressions(env)
- return super(LambdaNode, self).analyse_types(env)
- def generate_result_code(self, code):
- self.def_node.generate_execution_code(code)
- super(LambdaNode, self).generate_result_code(code)
- class GeneratorExpressionNode(LambdaNode):
- # A generator expression, e.g. (i for i in range(10))
- #
- # Result is a generator.
- #
- # loop ForStatNode the for-loop, containing a YieldExprNode
- # def_node DefNode the underlying generator 'def' node
- name = StringEncoding.EncodedString('genexpr')
- binding = False
- def analyse_declarations(self, env):
- self.genexpr_name = env.next_id('genexpr')
- super(GeneratorExpressionNode, self).analyse_declarations(env)
- # No pymethdef required
- self.def_node.pymethdef_required = False
- self.def_node.py_wrapper_required = False
- self.def_node.is_cyfunction = False
- # Force genexpr signature
- self.def_node.entry.signature = TypeSlots.pyfunction_noargs
- def generate_result_code(self, code):
- code.putln(
- '%s = %s(%s); %s' % (
- self.result(),
- self.def_node.entry.pyfunc_cname,
- self.self_result_code(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class YieldExprNode(ExprNode):
- # Yield expression node
- #
- # arg ExprNode the value to return from the generator
- # label_num integer yield label number
- # is_yield_from boolean is a YieldFromExprNode to delegate to another generator
- subexprs = ['arg']
- type = py_object_type
- label_num = 0
- is_yield_from = False
- is_await = False
- in_async_gen = False
- expr_keyword = 'yield'
- def analyse_types(self, env):
- if not self.label_num or (self.is_yield_from and self.in_async_gen):
- error(self.pos, "'%s' not supported here" % self.expr_keyword)
- self.is_temp = 1
- if self.arg is not None:
- self.arg = self.arg.analyse_types(env)
- if not self.arg.type.is_pyobject:
- self.coerce_yield_argument(env)
- return self
- def coerce_yield_argument(self, env):
- self.arg = self.arg.coerce_to_pyobject(env)
- def generate_evaluation_code(self, code):
- if self.arg:
- self.arg.generate_evaluation_code(code)
- self.arg.make_owned_reference(code)
- code.putln(
- "%s = %s;" % (
- Naming.retval_cname,
- self.arg.result_as(py_object_type)))
- self.arg.generate_post_assignment_code(code)
- self.arg.free_temps(code)
- else:
- code.put_init_to_py_none(Naming.retval_cname, py_object_type)
- self.generate_yield_code(code)
- def generate_yield_code(self, code):
- """
- Generate the code to return the argument in 'Naming.retval_cname'
- and to continue at the yield label.
- """
- label_num, label_name = code.new_yield_label(
- self.expr_keyword.replace(' ', '_'))
- code.use_label(label_name)
- saved = []
- code.funcstate.closure_temps.reset()
- for cname, type, manage_ref in code.funcstate.temps_in_use():
- save_cname = code.funcstate.closure_temps.allocate_temp(type)
- saved.append((cname, save_cname, type))
- if type.is_pyobject:
- code.put_xgiveref(cname)
- code.putln('%s->%s = %s;' % (Naming.cur_scope_cname, save_cname, cname))
- code.put_xgiveref(Naming.retval_cname)
- profile = code.globalstate.directives['profile']
- linetrace = code.globalstate.directives['linetrace']
- if profile or linetrace:
- code.put_trace_return(Naming.retval_cname,
- nogil=not code.funcstate.gil_owned)
- code.put_finish_refcount_context()
- if code.funcstate.current_except is not None:
- # inside of an except block => save away currently handled exception
- code.putln("__Pyx_Coroutine_SwapException(%s);" % Naming.generator_cname)
- else:
- # no exceptions being handled => restore exception state of caller
- code.putln("__Pyx_Coroutine_ResetAndClearException(%s);" % Naming.generator_cname)
- code.putln("/* return from %sgenerator, %sing value */" % (
- 'async ' if self.in_async_gen else '',
- 'await' if self.is_await else 'yield'))
- code.putln("%s->resume_label = %d;" % (
- Naming.generator_cname, label_num))
- if self.in_async_gen and not self.is_await:
- # __Pyx__PyAsyncGenValueWrapperNew() steals a reference to the return value
- code.putln("return __Pyx__PyAsyncGenValueWrapperNew(%s);" % Naming.retval_cname)
- else:
- code.putln("return %s;" % Naming.retval_cname)
- code.put_label(label_name)
- for cname, save_cname, type in saved:
- code.putln('%s = %s->%s;' % (cname, Naming.cur_scope_cname, save_cname))
- if type.is_pyobject:
- code.putln('%s->%s = 0;' % (Naming.cur_scope_cname, save_cname))
- code.put_xgotref(cname)
- self.generate_sent_value_handling_code(code, Naming.sent_value_cname)
- if self.result_is_used:
- self.allocate_temp_result(code)
- code.put('%s = %s; ' % (self.result(), Naming.sent_value_cname))
- code.put_incref(self.result(), py_object_type)
- def generate_sent_value_handling_code(self, code, value_cname):
- code.putln(code.error_goto_if_null(value_cname, self.pos))
- class _YieldDelegationExprNode(YieldExprNode):
- def yield_from_func(self, code):
- raise NotImplementedError()
- def generate_evaluation_code(self, code, source_cname=None, decref_source=False):
- if source_cname is None:
- self.arg.generate_evaluation_code(code)
- code.putln("%s = %s(%s, %s);" % (
- Naming.retval_cname,
- self.yield_from_func(code),
- Naming.generator_cname,
- self.arg.py_result() if source_cname is None else source_cname))
- if source_cname is None:
- self.arg.generate_disposal_code(code)
- self.arg.free_temps(code)
- elif decref_source:
- code.put_decref_clear(source_cname, py_object_type)
- code.put_xgotref(Naming.retval_cname)
- code.putln("if (likely(%s)) {" % Naming.retval_cname)
- self.generate_yield_code(code)
- code.putln("} else {")
- # either error or sub-generator has normally terminated: return value => node result
- if self.result_is_used:
- self.fetch_iteration_result(code)
- else:
- self.handle_iteration_exception(code)
- code.putln("}")
- def fetch_iteration_result(self, code):
- # YieldExprNode has allocated the result temp for us
- code.putln("%s = NULL;" % self.result())
- code.put_error_if_neg(self.pos, "__Pyx_PyGen_FetchStopIterationValue(&%s)" % self.result())
- code.put_gotref(self.result())
- def handle_iteration_exception(self, code):
- code.putln("PyObject* exc_type = __Pyx_PyErr_Occurred();")
- code.putln("if (exc_type) {")
- code.putln("if (likely(exc_type == PyExc_StopIteration || (exc_type != PyExc_GeneratorExit &&"
- " __Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration)))) PyErr_Clear();")
- code.putln("else %s" % code.error_goto(self.pos))
- code.putln("}")
- class YieldFromExprNode(_YieldDelegationExprNode):
- # "yield from GEN" expression
- is_yield_from = True
- expr_keyword = 'yield from'
- def coerce_yield_argument(self, env):
- if not self.arg.type.is_string:
- # FIXME: support C arrays and C++ iterators?
- error(self.pos, "yielding from non-Python object not supported")
- self.arg = self.arg.coerce_to_pyobject(env)
- def yield_from_func(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("GeneratorYieldFrom", "Coroutine.c"))
- return "__Pyx_Generator_Yield_From"
- class AwaitExprNode(_YieldDelegationExprNode):
- # 'await' expression node
- #
- # arg ExprNode the Awaitable value to await
- # label_num integer yield label number
- is_await = True
- expr_keyword = 'await'
- def coerce_yield_argument(self, env):
- if self.arg is not None:
- # FIXME: use same check as in YieldFromExprNode.coerce_yield_argument() ?
- self.arg = self.arg.coerce_to_pyobject(env)
- def yield_from_func(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("CoroutineYieldFrom", "Coroutine.c"))
- return "__Pyx_Coroutine_Yield_From"
- class AwaitIterNextExprNode(AwaitExprNode):
- # 'await' expression node as part of 'async for' iteration
- #
- # Breaks out of loop on StopAsyncIteration exception.
- def _generate_break(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("StopAsyncIteration", "Coroutine.c"))
- code.putln("PyObject* exc_type = __Pyx_PyErr_Occurred();")
- code.putln("if (unlikely(exc_type && (exc_type == __Pyx_PyExc_StopAsyncIteration || ("
- " exc_type != PyExc_StopIteration && exc_type != PyExc_GeneratorExit &&"
- " __Pyx_PyErr_GivenExceptionMatches(exc_type, __Pyx_PyExc_StopAsyncIteration))))) {")
- code.putln("PyErr_Clear();")
- code.putln("break;")
- code.putln("}")
- def fetch_iteration_result(self, code):
- assert code.break_label, "AwaitIterNextExprNode outside of 'async for' loop"
- self._generate_break(code)
- super(AwaitIterNextExprNode, self).fetch_iteration_result(code)
- def generate_sent_value_handling_code(self, code, value_cname):
- assert code.break_label, "AwaitIterNextExprNode outside of 'async for' loop"
- code.putln("if (unlikely(!%s)) {" % value_cname)
- self._generate_break(code)
- # all non-break exceptions are errors, as in parent class
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- class GlobalsExprNode(AtomicExprNode):
- type = dict_type
- is_temp = 1
- def analyse_types(self, env):
- env.use_utility_code(Builtin.globals_utility_code)
- return self
- gil_message = "Constructing globals dict"
- def may_be_none(self):
- return False
- def generate_result_code(self, code):
- code.putln('%s = __Pyx_Globals(); %s' % (
- self.result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- class LocalsDictItemNode(DictItemNode):
- def analyse_types(self, env):
- self.key = self.key.analyse_types(env)
- self.value = self.value.analyse_types(env)
- self.key = self.key.coerce_to_pyobject(env)
- if self.value.type.can_coerce_to_pyobject(env):
- self.value = self.value.coerce_to_pyobject(env)
- else:
- self.value = None
- return self
- class FuncLocalsExprNode(DictNode):
- def __init__(self, pos, env):
- local_vars = sorted([
- entry.name for entry in env.entries.values() if entry.name])
- items = [LocalsDictItemNode(
- pos, key=IdentifierStringNode(pos, value=var),
- value=NameNode(pos, name=var, allow_null=True))
- for var in local_vars]
- DictNode.__init__(self, pos, key_value_pairs=items,
- exclude_null_values=True)
- def analyse_types(self, env):
- node = super(FuncLocalsExprNode, self).analyse_types(env)
- node.key_value_pairs = [ i for i in node.key_value_pairs
- if i.value is not None ]
- return node
- class PyClassLocalsExprNode(AtomicExprNode):
- def __init__(self, pos, pyclass_dict):
- AtomicExprNode.__init__(self, pos)
- self.pyclass_dict = pyclass_dict
- def analyse_types(self, env):
- self.type = self.pyclass_dict.type
- self.is_temp = False
- return self
- def may_be_none(self):
- return False
- def result(self):
- return self.pyclass_dict.result()
- def generate_result_code(self, code):
- pass
- def LocalsExprNode(pos, scope_node, env):
- if env.is_module_scope:
- return GlobalsExprNode(pos)
- if env.is_py_class_scope:
- return PyClassLocalsExprNode(pos, scope_node.dict)
- return FuncLocalsExprNode(pos, env)
- #-------------------------------------------------------------------
- #
- # Unary operator nodes
- #
- #-------------------------------------------------------------------
- compile_time_unary_operators = {
- 'not': operator.not_,
- '~': operator.inv,
- '-': operator.neg,
- '+': operator.pos,
- }
- class UnopNode(ExprNode):
- # operator string
- # operand ExprNode
- #
- # Processing during analyse_expressions phase:
- #
- # analyse_c_operation
- # Called when the operand is not a pyobject.
- # - Check operand type and coerce if needed.
- # - Determine result type and result code fragment.
- # - Allocate temporary for result if needed.
- subexprs = ['operand']
- infix = True
- def calculate_constant_result(self):
- func = compile_time_unary_operators[self.operator]
- self.constant_result = func(self.operand.constant_result)
- def compile_time_value(self, denv):
- func = compile_time_unary_operators.get(self.operator)
- if not func:
- error(self.pos,
- "Unary '%s' not supported in compile-time expression"
- % self.operator)
- operand = self.operand.compile_time_value(denv)
- try:
- return func(operand)
- except Exception as e:
- self.compile_time_value_error(e)
- def infer_type(self, env):
- operand_type = self.operand.infer_type(env)
- if operand_type.is_cpp_class or operand_type.is_ptr:
- cpp_type = operand_type.find_cpp_operation_type(self.operator)
- if cpp_type is not None:
- return cpp_type
- return self.infer_unop_type(env, operand_type)
- def infer_unop_type(self, env, operand_type):
- if operand_type.is_pyobject:
- return py_object_type
- else:
- return operand_type
- def may_be_none(self):
- if self.operand.type and self.operand.type.is_builtin_type:
- if self.operand.type is not type_type:
- return False
- return ExprNode.may_be_none(self)
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- if self.is_pythran_operation(env):
- self.type = PythranExpr(pythran_unaryop_type(self.operator, self.operand.type))
- self.is_temp = 1
- elif self.is_py_operation():
- self.coerce_operand_to_pyobject(env)
- self.type = py_object_type
- self.is_temp = 1
- elif self.is_cpp_operation():
- self.analyse_cpp_operation(env)
- else:
- self.analyse_c_operation(env)
- return self
- def check_const(self):
- return self.operand.check_const()
- def is_py_operation(self):
- return self.operand.type.is_pyobject or self.operand.type.is_ctuple
- def is_pythran_operation(self, env):
- np_pythran = has_np_pythran(env)
- op_type = self.operand.type
- return np_pythran and (op_type.is_buffer or op_type.is_pythran_expr)
- def nogil_check(self, env):
- if self.is_py_operation():
- self.gil_error()
- def is_cpp_operation(self):
- type = self.operand.type
- return type.is_cpp_class
- def coerce_operand_to_pyobject(self, env):
- self.operand = self.operand.coerce_to_pyobject(env)
- def generate_result_code(self, code):
- if self.type.is_pythran_expr:
- code.putln("// Pythran unaryop")
- code.putln("__Pyx_call_destructor(%s);" % self.result())
- code.putln("new (&%s) decltype(%s){%s%s};" % (
- self.result(),
- self.result(),
- self.operator,
- self.operand.pythran_result()))
- elif self.operand.type.is_pyobject:
- self.generate_py_operation_code(code)
- elif self.is_temp:
- if self.is_cpp_operation() and self.exception_check == '+':
- translate_cpp_exception(code, self.pos,
- "%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- else:
- code.putln("%s = %s %s;" % (self.result(), self.operator, self.operand.result()))
- def generate_py_operation_code(self, code):
- function = self.py_operation_function(code)
- code.putln(
- "%s = %s(%s); %s" % (
- self.result(),
- function,
- self.operand.py_result(),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- def type_error(self):
- if not self.operand.type.is_error:
- error(self.pos, "Invalid operand type for '%s' (%s)" %
- (self.operator, self.operand.type))
- self.type = PyrexTypes.error_type
- def analyse_cpp_operation(self, env, overload_check=True):
- entry = env.lookup_operator(self.operator, [self.operand])
- if overload_check and not entry:
- self.type_error()
- return
- if entry:
- self.exception_check = entry.type.exception_check
- self.exception_value = entry.type.exception_value
- if self.exception_check == '+':
- self.is_temp = True
- if self.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- else:
- self.exception_check = ''
- self.exception_value = ''
- cpp_type = self.operand.type.find_cpp_operation_type(self.operator)
- if overload_check and cpp_type is None:
- error(self.pos, "'%s' operator not defined for %s" % (
- self.operator, type))
- self.type_error()
- return
- self.type = cpp_type
- class NotNode(UnopNode):
- # 'not' operator
- #
- # operand ExprNode
- operator = '!'
- type = PyrexTypes.c_bint_type
- def calculate_constant_result(self):
- self.constant_result = not self.operand.constant_result
- def compile_time_value(self, denv):
- operand = self.operand.compile_time_value(denv)
- try:
- return not operand
- except Exception as e:
- self.compile_time_value_error(e)
- def infer_unop_type(self, env, operand_type):
- return PyrexTypes.c_bint_type
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- operand_type = self.operand.type
- if operand_type.is_cpp_class:
- self.analyse_cpp_operation(env)
- else:
- self.operand = self.operand.coerce_to_boolean(env)
- return self
- def calculate_result_code(self):
- return "(!%s)" % self.operand.result()
- class UnaryPlusNode(UnopNode):
- # unary '+' operator
- operator = '+'
- def analyse_c_operation(self, env):
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- def py_operation_function(self, code):
- return "PyNumber_Positive"
- def calculate_result_code(self):
- if self.is_cpp_operation():
- return "(+%s)" % self.operand.result()
- else:
- return self.operand.result()
- class UnaryMinusNode(UnopNode):
- # unary '-' operator
- operator = '-'
- def analyse_c_operation(self, env):
- if self.operand.type.is_numeric:
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- elif self.operand.type.is_enum:
- self.type = PyrexTypes.c_int_type
- else:
- self.type_error()
- if self.type.is_complex:
- self.infix = False
- def py_operation_function(self, code):
- return "PyNumber_Negative"
- def calculate_result_code(self):
- if self.infix:
- return "(-%s)" % self.operand.result()
- else:
- return "%s(%s)" % (self.operand.type.unary_op('-'), self.operand.result())
- def get_constant_c_result_code(self):
- value = self.operand.get_constant_c_result_code()
- if value:
- return "(-%s)" % value
- class TildeNode(UnopNode):
- # unary '~' operator
- def analyse_c_operation(self, env):
- if self.operand.type.is_int:
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- elif self.operand.type.is_enum:
- self.type = PyrexTypes.c_int_type
- else:
- self.type_error()
- def py_operation_function(self, code):
- return "PyNumber_Invert"
- def calculate_result_code(self):
- return "(~%s)" % self.operand.result()
- class CUnopNode(UnopNode):
- def is_py_operation(self):
- return False
- class DereferenceNode(CUnopNode):
- # unary * operator
- operator = '*'
- def infer_unop_type(self, env, operand_type):
- if operand_type.is_ptr:
- return operand_type.base_type
- else:
- return PyrexTypes.error_type
- def analyse_c_operation(self, env):
- if self.operand.type.is_ptr:
- self.type = self.operand.type.base_type
- else:
- self.type_error()
- def calculate_result_code(self):
- return "(*%s)" % self.operand.result()
- class DecrementIncrementNode(CUnopNode):
- # unary ++/-- operator
- def analyse_c_operation(self, env):
- if self.operand.type.is_numeric:
- self.type = PyrexTypes.widest_numeric_type(
- self.operand.type, PyrexTypes.c_int_type)
- elif self.operand.type.is_ptr:
- self.type = self.operand.type
- else:
- self.type_error()
- def calculate_result_code(self):
- if self.is_prefix:
- return "(%s%s)" % (self.operator, self.operand.result())
- else:
- return "(%s%s)" % (self.operand.result(), self.operator)
- def inc_dec_constructor(is_prefix, operator):
- return lambda pos, **kwds: DecrementIncrementNode(pos, is_prefix=is_prefix, operator=operator, **kwds)
- class AmpersandNode(CUnopNode):
- # The C address-of operator.
- #
- # operand ExprNode
- operator = '&'
- def infer_unop_type(self, env, operand_type):
- return PyrexTypes.c_ptr_type(operand_type)
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- argtype = self.operand.type
- if argtype.is_cpp_class:
- self.analyse_cpp_operation(env, overload_check=False)
- if not (argtype.is_cfunction or argtype.is_reference or self.operand.is_addressable()):
- if argtype.is_memoryviewslice:
- self.error("Cannot take address of memoryview slice")
- else:
- self.error("Taking address of non-lvalue (type %s)" % argtype)
- return self
- if argtype.is_pyobject:
- self.error("Cannot take address of Python %s" % (
- "variable '%s'" % self.operand.name if self.operand.is_name else
- "object attribute '%s'" % self.operand.attribute if self.operand.is_attribute else
- "object"))
- return self
- if not argtype.is_cpp_class or not self.type:
- self.type = PyrexTypes.c_ptr_type(argtype)
- return self
- def check_const(self):
- return self.operand.check_const_addr()
- def error(self, mess):
- error(self.pos, mess)
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- def calculate_result_code(self):
- return "(&%s)" % self.operand.result()
- def generate_result_code(self, code):
- if (self.operand.type.is_cpp_class and self.exception_check == '+'):
- translate_cpp_exception(code, self.pos,
- "%s = %s %s;" % (self.result(), self.operator, self.operand.result()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- unop_node_classes = {
- "+": UnaryPlusNode,
- "-": UnaryMinusNode,
- "~": TildeNode,
- }
- def unop_node(pos, operator, operand):
- # Construct unnop node of appropriate class for
- # given operator.
- if isinstance(operand, IntNode) and operator == '-':
- return IntNode(pos = operand.pos, value = str(-Utils.str_to_number(operand.value)),
- longness=operand.longness, unsigned=operand.unsigned)
- elif isinstance(operand, UnopNode) and operand.operator == operator in '+-':
- warning(pos, "Python has no increment/decrement operator: %s%sx == %s(%sx) == x" % ((operator,)*4), 5)
- return unop_node_classes[operator](pos,
- operator = operator,
- operand = operand)
- class TypecastNode(ExprNode):
- # C type cast
- #
- # operand ExprNode
- # base_type CBaseTypeNode
- # declarator CDeclaratorNode
- # typecheck boolean
- #
- # If used from a transform, one can if wanted specify the attribute
- # "type" directly and leave base_type and declarator to None
- subexprs = ['operand']
- base_type = declarator = type = None
- def type_dependencies(self, env):
- return ()
- def infer_type(self, env):
- if self.type is None:
- base_type = self.base_type.analyse(env)
- _, self.type = self.declarator.analyse(base_type, env)
- return self.type
- def analyse_types(self, env):
- if self.type is None:
- base_type = self.base_type.analyse(env)
- _, self.type = self.declarator.analyse(base_type, env)
- if self.operand.has_constant_result():
- # Must be done after self.type is resolved.
- self.calculate_constant_result()
- if self.type.is_cfunction:
- error(self.pos,
- "Cannot cast to a function type")
- self.type = PyrexTypes.error_type
- self.operand = self.operand.analyse_types(env)
- if self.type is PyrexTypes.c_bint_type:
- # short circuit this to a coercion
- return self.operand.coerce_to_boolean(env)
- to_py = self.type.is_pyobject
- from_py = self.operand.type.is_pyobject
- if from_py and not to_py and self.operand.is_ephemeral():
- if not self.type.is_numeric and not self.type.is_cpp_class:
- error(self.pos, "Casting temporary Python object to non-numeric non-Python type")
- if to_py and not from_py:
- if self.type is bytes_type and self.operand.type.is_int:
- return CoerceIntToBytesNode(self.operand, env)
- elif self.operand.type.can_coerce_to_pyobject(env):
- self.result_ctype = py_object_type
- self.operand = self.operand.coerce_to(self.type, env)
- else:
- if self.operand.type.is_ptr:
- if not (self.operand.type.base_type.is_void or self.operand.type.base_type.is_struct):
- error(self.pos, "Python objects cannot be cast from pointers of primitive types")
- else:
- # Should this be an error?
- warning(self.pos, "No conversion from %s to %s, python object pointer used." % (
- self.operand.type, self.type))
- self.operand = self.operand.coerce_to_simple(env)
- elif from_py and not to_py:
- if self.type.create_from_py_utility_code(env):
- self.operand = self.operand.coerce_to(self.type, env)
- elif self.type.is_ptr:
- if not (self.type.base_type.is_void or self.type.base_type.is_struct):
- error(self.pos, "Python objects cannot be cast to pointers of primitive types")
- else:
- warning(self.pos, "No conversion from %s to %s, python object pointer used." % (
- self.type, self.operand.type))
- elif from_py and to_py:
- if self.typecheck:
- self.operand = PyTypeTestNode(self.operand, self.type, env, notnone=True)
- elif isinstance(self.operand, SliceIndexNode):
- # This cast can influence the created type of string slices.
- self.operand = self.operand.coerce_to(self.type, env)
- elif self.type.is_complex and self.operand.type.is_complex:
- self.operand = self.operand.coerce_to_simple(env)
- elif self.operand.type.is_fused:
- self.operand = self.operand.coerce_to(self.type, env)
- #self.type = self.operand.type
- if self.type.is_ptr and self.type.base_type.is_cfunction and self.type.base_type.nogil:
- op_type = self.operand.type
- if op_type.is_ptr:
- op_type = op_type.base_type
- if op_type.is_cfunction and not op_type.nogil:
- warning(self.pos,
- "Casting a GIL-requiring function into a nogil function circumvents GIL validation", 1)
- return self
- def is_simple(self):
- # either temp or a C cast => no side effects other than the operand's
- return self.operand.is_simple()
- def is_ephemeral(self):
- # either temp or a C cast => no side effects other than the operand's
- return self.operand.is_ephemeral()
- def nonlocally_immutable(self):
- return self.is_temp or self.operand.nonlocally_immutable()
- def nogil_check(self, env):
- if self.type and self.type.is_pyobject and self.is_temp:
- self.gil_error()
- def check_const(self):
- return self.operand.check_const()
- def calculate_constant_result(self):
- self.constant_result = self.calculate_result_code(self.operand.constant_result)
- def calculate_result_code(self, operand_result = None):
- if operand_result is None:
- operand_result = self.operand.result()
- if self.type.is_complex:
- operand_result = self.operand.result()
- if self.operand.type.is_complex:
- real_part = self.type.real_type.cast_code("__Pyx_CREAL(%s)" % operand_result)
- imag_part = self.type.real_type.cast_code("__Pyx_CIMAG(%s)" % operand_result)
- else:
- real_part = self.type.real_type.cast_code(operand_result)
- imag_part = "0"
- return "%s(%s, %s)" % (
- self.type.from_parts,
- real_part,
- imag_part)
- else:
- return self.type.cast_code(operand_result)
- def get_constant_c_result_code(self):
- operand_result = self.operand.get_constant_c_result_code()
- if operand_result:
- return self.type.cast_code(operand_result)
- def result_as(self, type):
- if self.type.is_pyobject and not self.is_temp:
- # Optimise away some unnecessary casting
- return self.operand.result_as(type)
- else:
- return ExprNode.result_as(self, type)
- def generate_result_code(self, code):
- if self.is_temp:
- code.putln(
- "%s = (PyObject *)%s;" % (
- self.result(),
- self.operand.result()))
- code.put_incref(self.result(), self.ctype())
- ERR_START = "Start may not be given"
- ERR_NOT_STOP = "Stop must be provided to indicate shape"
- ERR_STEPS = ("Strides may only be given to indicate contiguity. "
- "Consider slicing it after conversion")
- ERR_NOT_POINTER = "Can only create cython.array from pointer or array"
- ERR_BASE_TYPE = "Pointer base type does not match cython.array base type"
- class CythonArrayNode(ExprNode):
- """
- Used when a pointer of base_type is cast to a memoryviewslice with that
- base type. i.e.
- <int[:M:1, :N]> p
- creates a fortran-contiguous cython.array.
- We leave the type set to object so coercions to object are more efficient
- and less work. Acquiring a memoryviewslice from this will be just as
- efficient. ExprNode.coerce_to() will do the additional typecheck on
- self.compile_time_type
- This also handles <int[:, :]> my_c_array
- operand ExprNode the thing we're casting
- base_type_node MemoryViewSliceTypeNode the cast expression node
- """
- subexprs = ['operand', 'shapes']
- shapes = None
- is_temp = True
- mode = "c"
- array_dtype = None
- shape_type = PyrexTypes.c_py_ssize_t_type
- def analyse_types(self, env):
- from . import MemoryView
- self.operand = self.operand.analyse_types(env)
- if self.array_dtype:
- array_dtype = self.array_dtype
- else:
- array_dtype = self.base_type_node.base_type_node.analyse(env)
- axes = self.base_type_node.axes
- self.type = error_type
- self.shapes = []
- ndim = len(axes)
- # Base type of the pointer or C array we are converting
- base_type = self.operand.type
- if not self.operand.type.is_ptr and not self.operand.type.is_array:
- error(self.operand.pos, ERR_NOT_POINTER)
- return self
- # Dimension sizes of C array
- array_dimension_sizes = []
- if base_type.is_array:
- while base_type.is_array:
- array_dimension_sizes.append(base_type.size)
- base_type = base_type.base_type
- elif base_type.is_ptr:
- base_type = base_type.base_type
- else:
- error(self.pos, "unexpected base type %s found" % base_type)
- return self
- if not (base_type.same_as(array_dtype) or base_type.is_void):
- error(self.operand.pos, ERR_BASE_TYPE)
- return self
- elif self.operand.type.is_array and len(array_dimension_sizes) != ndim:
- error(self.operand.pos,
- "Expected %d dimensions, array has %d dimensions" %
- (ndim, len(array_dimension_sizes)))
- return self
- # Verify the start, stop and step values
- # In case of a C array, use the size of C array in each dimension to
- # get an automatic cast
- for axis_no, axis in enumerate(axes):
- if not axis.start.is_none:
- error(axis.start.pos, ERR_START)
- return self
- if axis.stop.is_none:
- if array_dimension_sizes:
- dimsize = array_dimension_sizes[axis_no]
- axis.stop = IntNode(self.pos, value=str(dimsize),
- constant_result=dimsize,
- type=PyrexTypes.c_int_type)
- else:
- error(axis.pos, ERR_NOT_STOP)
- return self
- axis.stop = axis.stop.analyse_types(env)
- shape = axis.stop.coerce_to(self.shape_type, env)
- if not shape.is_literal:
- shape.coerce_to_temp(env)
- self.shapes.append(shape)
- first_or_last = axis_no in (0, ndim - 1)
- if not axis.step.is_none and first_or_last:
- # '1' in the first or last dimension denotes F or C contiguity
- axis.step = axis.step.analyse_types(env)
- if (not axis.step.type.is_int and axis.step.is_literal and not
- axis.step.type.is_error):
- error(axis.step.pos, "Expected an integer literal")
- return self
- if axis.step.compile_time_value(env) != 1:
- error(axis.step.pos, ERR_STEPS)
- return self
- if axis_no == 0:
- self.mode = "fortran"
- elif not axis.step.is_none and not first_or_last:
- # step provided in some other dimension
- error(axis.step.pos, ERR_STEPS)
- return self
- if not self.operand.is_name:
- self.operand = self.operand.coerce_to_temp(env)
- axes = [('direct', 'follow')] * len(axes)
- if self.mode == "fortran":
- axes[0] = ('direct', 'contig')
- else:
- axes[-1] = ('direct', 'contig')
- self.coercion_type = PyrexTypes.MemoryViewSliceType(array_dtype, axes)
- self.coercion_type.validate_memslice_dtype(self.pos)
- self.type = self.get_cython_array_type(env)
- MemoryView.use_cython_array_utility_code(env)
- env.use_utility_code(MemoryView.typeinfo_to_format_code)
- return self
- def allocate_temp_result(self, code):
- if self.temp_code:
- raise RuntimeError("temp allocated multiple times")
- self.temp_code = code.funcstate.allocate_temp(self.type, True)
- def infer_type(self, env):
- return self.get_cython_array_type(env)
- def get_cython_array_type(self, env):
- cython_scope = env.global_scope().context.cython_scope
- cython_scope.load_cythonscope()
- return cython_scope.viewscope.lookup("array").type
- def generate_result_code(self, code):
- from . import Buffer
- shapes = [self.shape_type.cast_code(shape.result())
- for shape in self.shapes]
- dtype = self.coercion_type.dtype
- shapes_temp = code.funcstate.allocate_temp(py_object_type, True)
- format_temp = code.funcstate.allocate_temp(py_object_type, True)
- itemsize = "sizeof(%s)" % dtype.empty_declaration_code()
- type_info = Buffer.get_type_information_cname(code, dtype)
- if self.operand.type.is_ptr:
- code.putln("if (!%s) {" % self.operand.result())
- code.putln( 'PyErr_SetString(PyExc_ValueError,'
- '"Cannot create cython.array from NULL pointer");')
- code.putln(code.error_goto(self.operand.pos))
- code.putln("}")
- code.putln("%s = __pyx_format_from_typeinfo(&%s); %s" % (
- format_temp,
- type_info,
- code.error_goto_if_null(format_temp, self.pos),
- ))
- code.put_gotref(format_temp)
- buildvalue_fmt = " __PYX_BUILD_PY_SSIZE_T " * len(shapes)
- code.putln('%s = Py_BuildValue((char*) "(" %s ")", %s); %s' % (
- shapes_temp,
- buildvalue_fmt,
- ", ".join(shapes),
- code.error_goto_if_null(shapes_temp, self.pos),
- ))
- code.put_gotref(shapes_temp)
- tup = (self.result(), shapes_temp, itemsize, format_temp,
- self.mode, self.operand.result())
- code.putln('%s = __pyx_array_new('
- '%s, %s, PyBytes_AS_STRING(%s), '
- '(char *) "%s", (char *) %s);' % tup)
- code.putln(code.error_goto_if_null(self.result(), self.pos))
- code.put_gotref(self.result())
- def dispose(temp):
- code.put_decref_clear(temp, py_object_type)
- code.funcstate.release_temp(temp)
- dispose(shapes_temp)
- dispose(format_temp)
- @classmethod
- def from_carray(cls, src_node, env):
- """
- Given a C array type, return a CythonArrayNode
- """
- pos = src_node.pos
- base_type = src_node.type
- none_node = NoneNode(pos)
- axes = []
- while base_type.is_array:
- axes.append(SliceNode(pos, start=none_node, stop=none_node,
- step=none_node))
- base_type = base_type.base_type
- axes[-1].step = IntNode(pos, value="1", is_c_literal=True)
- memslicenode = Nodes.MemoryViewSliceTypeNode(pos, axes=axes,
- base_type_node=base_type)
- result = CythonArrayNode(pos, base_type_node=memslicenode,
- operand=src_node, array_dtype=base_type)
- result = result.analyse_types(env)
- return result
- class SizeofNode(ExprNode):
- # Abstract base class for sizeof(x) expression nodes.
- type = PyrexTypes.c_size_t_type
- def check_const(self):
- return True
- def generate_result_code(self, code):
- pass
- class SizeofTypeNode(SizeofNode):
- # C sizeof function applied to a type
- #
- # base_type CBaseTypeNode
- # declarator CDeclaratorNode
- subexprs = []
- arg_type = None
- def analyse_types(self, env):
- # we may have incorrectly interpreted a dotted name as a type rather than an attribute
- # this could be better handled by more uniformly treating types as runtime-available objects
- if 0 and self.base_type.module_path:
- path = self.base_type.module_path
- obj = env.lookup(path[0])
- if obj.as_module is None:
- operand = NameNode(pos=self.pos, name=path[0])
- for attr in path[1:]:
- operand = AttributeNode(pos=self.pos, obj=operand, attribute=attr)
- operand = AttributeNode(pos=self.pos, obj=operand, attribute=self.base_type.name)
- node = SizeofVarNode(self.pos, operand=operand).analyse_types(env)
- return node
- if self.arg_type is None:
- base_type = self.base_type.analyse(env)
- _, arg_type = self.declarator.analyse(base_type, env)
- self.arg_type = arg_type
- self.check_type()
- return self
- def check_type(self):
- arg_type = self.arg_type
- if not arg_type:
- return
- if arg_type.is_pyobject and not arg_type.is_extension_type:
- error(self.pos, "Cannot take sizeof Python object")
- elif arg_type.is_void:
- error(self.pos, "Cannot take sizeof void")
- elif not arg_type.is_complete():
- error(self.pos, "Cannot take sizeof incomplete type '%s'" % arg_type)
- def calculate_result_code(self):
- if self.arg_type.is_extension_type:
- # the size of the pointer is boring
- # we want the size of the actual struct
- arg_code = self.arg_type.declaration_code("", deref=1)
- else:
- arg_code = self.arg_type.empty_declaration_code()
- return "(sizeof(%s))" % arg_code
- class SizeofVarNode(SizeofNode):
- # C sizeof function applied to a variable
- #
- # operand ExprNode
- subexprs = ['operand']
- def analyse_types(self, env):
- # We may actually be looking at a type rather than a variable...
- # If we are, traditional analysis would fail...
- operand_as_type = self.operand.analyse_as_type(env)
- if operand_as_type:
- self.arg_type = operand_as_type
- if self.arg_type.is_fused:
- self.arg_type = self.arg_type.specialize(env.fused_to_specific)
- self.__class__ = SizeofTypeNode
- self.check_type()
- else:
- self.operand = self.operand.analyse_types(env)
- return self
- def calculate_result_code(self):
- return "(sizeof(%s))" % self.operand.result()
- def generate_result_code(self, code):
- pass
- class TypeidNode(ExprNode):
- # C++ typeid operator applied to a type or variable
- #
- # operand ExprNode
- # arg_type ExprNode
- # is_variable boolean
- type = PyrexTypes.error_type
- subexprs = ['operand']
- arg_type = None
- is_variable = None
- is_temp = 1
- def get_type_info_type(self, env):
- env_module = env
- while not env_module.is_module_scope:
- env_module = env_module.outer_scope
- typeinfo_module = env_module.find_module('libcpp.typeinfo', self.pos)
- typeinfo_entry = typeinfo_module.lookup('type_info')
- return PyrexTypes.CFakeReferenceType(PyrexTypes.c_const_type(typeinfo_entry.type))
- cpp_message = 'typeid operator'
- def analyse_types(self, env):
- self.cpp_check(env)
- type_info = self.get_type_info_type(env)
- if not type_info:
- self.error("The 'libcpp.typeinfo' module must be cimported to use the typeid() operator")
- return self
- self.type = type_info
- as_type = self.operand.analyse_as_type(env)
- if as_type:
- self.arg_type = as_type
- self.is_type = True
- else:
- self.arg_type = self.operand.analyse_types(env)
- self.is_type = False
- if self.arg_type.type.is_pyobject:
- self.error("Cannot use typeid on a Python object")
- return self
- elif self.arg_type.type.is_void:
- self.error("Cannot use typeid on void")
- return self
- elif not self.arg_type.type.is_complete():
- self.error("Cannot use typeid on incomplete type '%s'" % self.arg_type.type)
- return self
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- return self
- def error(self, mess):
- error(self.pos, mess)
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- def check_const(self):
- return True
- def calculate_result_code(self):
- return self.temp_code
- def generate_result_code(self, code):
- if self.is_type:
- arg_code = self.arg_type.empty_declaration_code()
- else:
- arg_code = self.arg_type.result()
- translate_cpp_exception(code, self.pos,
- "%s = typeid(%s);" % (self.temp_code, arg_code),
- None, None, self.in_nogil_context)
- class TypeofNode(ExprNode):
- # Compile-time type of an expression, as a string.
- #
- # operand ExprNode
- # literal StringNode # internal
- literal = None
- type = py_object_type
- subexprs = ['literal'] # 'operand' will be ignored after type analysis!
- def analyse_types(self, env):
- self.operand = self.operand.analyse_types(env)
- value = StringEncoding.EncodedString(str(self.operand.type)) #self.operand.type.typeof_name())
- literal = StringNode(self.pos, value=value)
- literal = literal.analyse_types(env)
- self.literal = literal.coerce_to_pyobject(env)
- return self
- def analyse_as_type(self, env):
- self.operand = self.operand.analyse_types(env)
- return self.operand.type
- def may_be_none(self):
- return False
- def generate_evaluation_code(self, code):
- self.literal.generate_evaluation_code(code)
- def calculate_result_code(self):
- return self.literal.calculate_result_code()
- #-------------------------------------------------------------------
- #
- # Binary operator nodes
- #
- #-------------------------------------------------------------------
- try:
- matmul_operator = operator.matmul
- except AttributeError:
- def matmul_operator(a, b):
- try:
- func = a.__matmul__
- except AttributeError:
- func = b.__rmatmul__
- return func(a, b)
- compile_time_binary_operators = {
- '<': operator.lt,
- '<=': operator.le,
- '==': operator.eq,
- '!=': operator.ne,
- '>=': operator.ge,
- '>': operator.gt,
- 'is': operator.is_,
- 'is_not': operator.is_not,
- '+': operator.add,
- '&': operator.and_,
- '/': operator.truediv,
- '//': operator.floordiv,
- '<<': operator.lshift,
- '%': operator.mod,
- '*': operator.mul,
- '|': operator.or_,
- '**': operator.pow,
- '>>': operator.rshift,
- '-': operator.sub,
- '^': operator.xor,
- '@': matmul_operator,
- 'in': lambda x, seq: x in seq,
- 'not_in': lambda x, seq: x not in seq,
- }
- def get_compile_time_binop(node):
- func = compile_time_binary_operators.get(node.operator)
- if not func:
- error(node.pos,
- "Binary '%s' not supported in compile-time expression"
- % node.operator)
- return func
- class BinopNode(ExprNode):
- # operator string
- # operand1 ExprNode
- # operand2 ExprNode
- #
- # Processing during analyse_expressions phase:
- #
- # analyse_c_operation
- # Called when neither operand is a pyobject.
- # - Check operand types and coerce if needed.
- # - Determine result type and result code fragment.
- # - Allocate temporary for result if needed.
- subexprs = ['operand1', 'operand2']
- inplace = False
- def calculate_constant_result(self):
- func = compile_time_binary_operators[self.operator]
- self.constant_result = func(
- self.operand1.constant_result,
- self.operand2.constant_result)
- def compile_time_value(self, denv):
- func = get_compile_time_binop(self)
- operand1 = self.operand1.compile_time_value(denv)
- operand2 = self.operand2.compile_time_value(denv)
- try:
- return func(operand1, operand2)
- except Exception as e:
- self.compile_time_value_error(e)
- def infer_type(self, env):
- return self.result_type(self.operand1.infer_type(env),
- self.operand2.infer_type(env), env)
- def analyse_types(self, env):
- self.operand1 = self.operand1.analyse_types(env)
- self.operand2 = self.operand2.analyse_types(env)
- self.analyse_operation(env)
- return self
- def analyse_operation(self, env):
- if self.is_pythran_operation(env):
- self.type = self.result_type(self.operand1.type,
- self.operand2.type, env)
- assert self.type.is_pythran_expr
- self.is_temp = 1
- elif self.is_py_operation():
- self.coerce_operands_to_pyobjects(env)
- self.type = self.result_type(self.operand1.type,
- self.operand2.type, env)
- assert self.type.is_pyobject
- self.is_temp = 1
- elif self.is_cpp_operation():
- self.analyse_cpp_operation(env)
- else:
- self.analyse_c_operation(env)
- def is_py_operation(self):
- return self.is_py_operation_types(self.operand1.type, self.operand2.type)
- def is_py_operation_types(self, type1, type2):
- return type1.is_pyobject or type2.is_pyobject or type1.is_ctuple or type2.is_ctuple
- def is_pythran_operation(self, env):
- return self.is_pythran_operation_types(self.operand1.type, self.operand2.type, env)
- def is_pythran_operation_types(self, type1, type2, env):
- # Support only expr op supported_type, or supported_type op expr
- return has_np_pythran(env) and \
- (is_pythran_supported_operation_type(type1) and is_pythran_supported_operation_type(type2)) and \
- (is_pythran_expr(type1) or is_pythran_expr(type2))
- def is_cpp_operation(self):
- return (self.operand1.type.is_cpp_class
- or self.operand2.type.is_cpp_class)
- def analyse_cpp_operation(self, env):
- entry = env.lookup_operator(self.operator, [self.operand1, self.operand2])
- if not entry:
- self.type_error()
- return
- func_type = entry.type
- self.exception_check = func_type.exception_check
- self.exception_value = func_type.exception_value
- if self.exception_check == '+':
- # Used by NumBinopNodes to break up expressions involving multiple
- # operators so that exceptions can be handled properly.
- self.is_temp = 1
- if self.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- if func_type.is_ptr:
- func_type = func_type.base_type
- if len(func_type.args) == 1:
- self.operand2 = self.operand2.coerce_to(func_type.args[0].type, env)
- else:
- self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
- self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
- self.type = func_type.return_type
- def result_type(self, type1, type2, env):
- if self.is_pythran_operation_types(type1, type2, env):
- return PythranExpr(pythran_binop_type(self.operator, type1, type2))
- if self.is_py_operation_types(type1, type2):
- if type2.is_string:
- type2 = Builtin.bytes_type
- elif type2.is_pyunicode_ptr:
- type2 = Builtin.unicode_type
- if type1.is_string:
- type1 = Builtin.bytes_type
- elif type1.is_pyunicode_ptr:
- type1 = Builtin.unicode_type
- if type1.is_builtin_type or type2.is_builtin_type:
- if type1 is type2 and self.operator in '**%+|&^':
- # FIXME: at least these operators should be safe - others?
- return type1
- result_type = self.infer_builtin_types_operation(type1, type2)
- if result_type is not None:
- return result_type
- return py_object_type
- elif type1.is_error or type2.is_error:
- return PyrexTypes.error_type
- else:
- return self.compute_c_result_type(type1, type2)
- def infer_builtin_types_operation(self, type1, type2):
- return None
- def nogil_check(self, env):
- if self.is_py_operation():
- self.gil_error()
- def coerce_operands_to_pyobjects(self, env):
- self.operand1 = self.operand1.coerce_to_pyobject(env)
- self.operand2 = self.operand2.coerce_to_pyobject(env)
- def check_const(self):
- return self.operand1.check_const() and self.operand2.check_const()
- def is_ephemeral(self):
- return (super(BinopNode, self).is_ephemeral() or
- self.operand1.is_ephemeral() or self.operand2.is_ephemeral())
- def generate_result_code(self, code):
- if self.type.is_pythran_expr:
- code.putln("// Pythran binop")
- code.putln("__Pyx_call_destructor(%s);" % self.result())
- if self.operator == '**':
- code.putln("new (&%s) decltype(%s){pythonic::numpy::functor::power{}(%s, %s)};" % (
- self.result(),
- self.result(),
- self.operand1.pythran_result(),
- self.operand2.pythran_result()))
- else:
- code.putln("new (&%s) decltype(%s){%s %s %s};" % (
- self.result(),
- self.result(),
- self.operand1.pythran_result(),
- self.operator,
- self.operand2.pythran_result()))
- elif self.operand1.type.is_pyobject:
- function = self.py_operation_function(code)
- if self.operator == '**':
- extra_args = ", Py_None"
- else:
- extra_args = ""
- code.putln(
- "%s = %s(%s, %s%s); %s" % (
- self.result(),
- function,
- self.operand1.py_result(),
- self.operand2.py_result(),
- extra_args,
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- elif self.is_temp:
- # C++ overloaded operators with exception values are currently all
- # handled through temporaries.
- if self.is_cpp_operation() and self.exception_check == '+':
- translate_cpp_exception(code, self.pos,
- "%s = %s;" % (self.result(), self.calculate_result_code()),
- self.result() if self.type.is_pyobject else None,
- self.exception_value, self.in_nogil_context)
- else:
- code.putln("%s = %s;" % (self.result(), self.calculate_result_code()))
- def type_error(self):
- if not (self.operand1.type.is_error
- or self.operand2.type.is_error):
- error(self.pos, "Invalid operand types for '%s' (%s; %s)" %
- (self.operator, self.operand1.type,
- self.operand2.type))
- self.type = PyrexTypes.error_type
- class CBinopNode(BinopNode):
- def analyse_types(self, env):
- node = BinopNode.analyse_types(self, env)
- if node.is_py_operation():
- node.type = PyrexTypes.error_type
- return node
- def py_operation_function(self, code):
- return ""
- def calculate_result_code(self):
- return "(%s %s %s)" % (
- self.operand1.result(),
- self.operator,
- self.operand2.result())
- def compute_c_result_type(self, type1, type2):
- cpp_type = None
- if type1.is_cpp_class or type1.is_ptr:
- cpp_type = type1.find_cpp_operation_type(self.operator, type2)
- if cpp_type is None and (type2.is_cpp_class or type2.is_ptr):
- cpp_type = type2.find_cpp_operation_type(self.operator, type1)
- # FIXME: do we need to handle other cases here?
- return cpp_type
- def c_binop_constructor(operator):
- def make_binop_node(pos, **operands):
- return CBinopNode(pos, operator=operator, **operands)
- return make_binop_node
- class NumBinopNode(BinopNode):
- # Binary operation taking numeric arguments.
- infix = True
- overflow_check = False
- overflow_bit_node = None
- def analyse_c_operation(self, env):
- type1 = self.operand1.type
- type2 = self.operand2.type
- self.type = self.compute_c_result_type(type1, type2)
- if not self.type:
- self.type_error()
- return
- if self.type.is_complex:
- self.infix = False
- if (self.type.is_int
- and env.directives['overflowcheck']
- and self.operator in self.overflow_op_names):
- if (self.operator in ('+', '*')
- and self.operand1.has_constant_result()
- and not self.operand2.has_constant_result()):
- self.operand1, self.operand2 = self.operand2, self.operand1
- self.overflow_check = True
- self.overflow_fold = env.directives['overflowcheck.fold']
- self.func = self.type.overflow_check_binop(
- self.overflow_op_names[self.operator],
- env,
- const_rhs = self.operand2.has_constant_result())
- self.is_temp = True
- if not self.infix or (type1.is_numeric and type2.is_numeric):
- self.operand1 = self.operand1.coerce_to(self.type, env)
- self.operand2 = self.operand2.coerce_to(self.type, env)
- def compute_c_result_type(self, type1, type2):
- if self.c_types_okay(type1, type2):
- widest_type = PyrexTypes.widest_numeric_type(type1, type2)
- if widest_type is PyrexTypes.c_bint_type:
- if self.operator not in '|^&':
- # False + False == 0 # not False!
- widest_type = PyrexTypes.c_int_type
- else:
- widest_type = PyrexTypes.widest_numeric_type(
- widest_type, PyrexTypes.c_int_type)
- return widest_type
- else:
- return None
- def may_be_none(self):
- if self.type and self.type.is_builtin_type:
- # if we know the result type, we know the operation, so it can't be None
- return False
- type1 = self.operand1.type
- type2 = self.operand2.type
- if type1 and type1.is_builtin_type and type2 and type2.is_builtin_type:
- # XXX: I can't think of any case where a binary operation
- # on builtin types evaluates to None - add a special case
- # here if there is one.
- return False
- return super(NumBinopNode, self).may_be_none()
- def get_constant_c_result_code(self):
- value1 = self.operand1.get_constant_c_result_code()
- value2 = self.operand2.get_constant_c_result_code()
- if value1 and value2:
- return "(%s %s %s)" % (value1, self.operator, value2)
- else:
- return None
- def c_types_okay(self, type1, type2):
- #print "NumBinopNode.c_types_okay:", type1, type2 ###
- return (type1.is_numeric or type1.is_enum) \
- and (type2.is_numeric or type2.is_enum)
- def generate_evaluation_code(self, code):
- if self.overflow_check:
- self.overflow_bit_node = self
- self.overflow_bit = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
- code.putln("%s = 0;" % self.overflow_bit)
- super(NumBinopNode, self).generate_evaluation_code(code)
- if self.overflow_check:
- code.putln("if (unlikely(%s)) {" % self.overflow_bit)
- code.putln('PyErr_SetString(PyExc_OverflowError, "value too large");')
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- code.funcstate.release_temp(self.overflow_bit)
- def calculate_result_code(self):
- if self.overflow_bit_node is not None:
- return "%s(%s, %s, &%s)" % (
- self.func,
- self.operand1.result(),
- self.operand2.result(),
- self.overflow_bit_node.overflow_bit)
- elif self.type.is_cpp_class or self.infix:
- if is_pythran_expr(self.type):
- result1, result2 = self.operand1.pythran_result(), self.operand2.pythran_result()
- else:
- result1, result2 = self.operand1.result(), self.operand2.result()
- return "(%s %s %s)" % (result1, self.operator, result2)
- else:
- func = self.type.binary_op(self.operator)
- if func is None:
- error(self.pos, "binary operator %s not supported for %s" % (self.operator, self.type))
- return "%s(%s, %s)" % (
- func,
- self.operand1.result(),
- self.operand2.result())
- def is_py_operation_types(self, type1, type2):
- return (type1.is_unicode_char or
- type2.is_unicode_char or
- BinopNode.is_py_operation_types(self, type1, type2))
- def py_operation_function(self, code):
- function_name = self.py_functions[self.operator]
- if self.inplace:
- function_name = function_name.replace('PyNumber_', 'PyNumber_InPlace')
- return function_name
- py_functions = {
- "|": "PyNumber_Or",
- "^": "PyNumber_Xor",
- "&": "PyNumber_And",
- "<<": "PyNumber_Lshift",
- ">>": "PyNumber_Rshift",
- "+": "PyNumber_Add",
- "-": "PyNumber_Subtract",
- "*": "PyNumber_Multiply",
- "@": "__Pyx_PyNumber_MatrixMultiply",
- "/": "__Pyx_PyNumber_Divide",
- "//": "PyNumber_FloorDivide",
- "%": "PyNumber_Remainder",
- "**": "PyNumber_Power",
- }
- overflow_op_names = {
- "+": "add",
- "-": "sub",
- "*": "mul",
- "<<": "lshift",
- }
- class IntBinopNode(NumBinopNode):
- # Binary operation taking integer arguments.
- def c_types_okay(self, type1, type2):
- #print "IntBinopNode.c_types_okay:", type1, type2 ###
- return (type1.is_int or type1.is_enum) \
- and (type2.is_int or type2.is_enum)
- class AddNode(NumBinopNode):
- # '+' operator.
- def is_py_operation_types(self, type1, type2):
- if type1.is_string and type2.is_string or type1.is_pyunicode_ptr and type2.is_pyunicode_ptr:
- return 1
- else:
- return NumBinopNode.is_py_operation_types(self, type1, type2)
- def infer_builtin_types_operation(self, type1, type2):
- # b'abc' + 'abc' raises an exception in Py3,
- # so we can safely infer the Py2 type for bytes here
- string_types = (bytes_type, bytearray_type, str_type, basestring_type, unicode_type)
- if type1 in string_types and type2 in string_types:
- return string_types[max(string_types.index(type1),
- string_types.index(type2))]
- return None
- def compute_c_result_type(self, type1, type2):
- #print "AddNode.compute_c_result_type:", type1, self.operator, type2 ###
- if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
- return type1
- elif (type2.is_ptr or type2.is_array) and (type1.is_int or type1.is_enum):
- return type2
- else:
- return NumBinopNode.compute_c_result_type(
- self, type1, type2)
- def py_operation_function(self, code):
- type1, type2 = self.operand1.type, self.operand2.type
- if type1 is unicode_type or type2 is unicode_type:
- if type1 in (unicode_type, str_type) and type2 in (unicode_type, str_type):
- is_unicode_concat = True
- elif isinstance(self.operand1, FormattedValueNode) or isinstance(self.operand2, FormattedValueNode):
- # Assume that even if we don't know the second type, it's going to be a string.
- is_unicode_concat = True
- else:
- # Operation depends on the second type.
- is_unicode_concat = False
- if is_unicode_concat:
- if self.operand1.may_be_none() or self.operand2.may_be_none():
- return '__Pyx_PyUnicode_ConcatSafe'
- else:
- return '__Pyx_PyUnicode_Concat'
- return super(AddNode, self).py_operation_function(code)
- class SubNode(NumBinopNode):
- # '-' operator.
- def compute_c_result_type(self, type1, type2):
- if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
- return type1
- elif (type1.is_ptr or type1.is_array) and (type2.is_ptr or type2.is_array):
- return PyrexTypes.c_ptrdiff_t_type
- else:
- return NumBinopNode.compute_c_result_type(
- self, type1, type2)
- class MulNode(NumBinopNode):
- # '*' operator.
- def is_py_operation_types(self, type1, type2):
- if ((type1.is_string and type2.is_int) or
- (type2.is_string and type1.is_int)):
- return 1
- else:
- return NumBinopNode.is_py_operation_types(self, type1, type2)
- def infer_builtin_types_operation(self, type1, type2):
- # let's assume that whatever builtin type you multiply a string with
- # will either return a string of the same type or fail with an exception
- string_types = (bytes_type, bytearray_type, str_type, basestring_type, unicode_type)
- if type1 in string_types and type2.is_builtin_type:
- return type1
- if type2 in string_types and type1.is_builtin_type:
- return type2
- # multiplication of containers/numbers with an integer value
- # always (?) returns the same type
- if type1.is_int:
- return type2
- if type2.is_int:
- return type1
- return None
- class MatMultNode(NumBinopNode):
- # '@' operator.
- def is_py_operation_types(self, type1, type2):
- return True
- def generate_evaluation_code(self, code):
- code.globalstate.use_utility_code(UtilityCode.load_cached("MatrixMultiply", "ObjectHandling.c"))
- super(MatMultNode, self).generate_evaluation_code(code)
- class DivNode(NumBinopNode):
- # '/' or '//' operator.
- cdivision = None
- truedivision = None # == "unknown" if operator == '/'
- ctruedivision = False
- cdivision_warnings = False
- zerodivision_check = None
- def find_compile_time_binary_operator(self, op1, op2):
- func = compile_time_binary_operators[self.operator]
- if self.operator == '/' and self.truedivision is None:
- # => true div for floats, floor div for integers
- if isinstance(op1, _py_int_types) and isinstance(op2, _py_int_types):
- func = compile_time_binary_operators['//']
- return func
- def calculate_constant_result(self):
- op1 = self.operand1.constant_result
- op2 = self.operand2.constant_result
- func = self.find_compile_time_binary_operator(op1, op2)
- self.constant_result = func(
- self.operand1.constant_result,
- self.operand2.constant_result)
- def compile_time_value(self, denv):
- operand1 = self.operand1.compile_time_value(denv)
- operand2 = self.operand2.compile_time_value(denv)
- try:
- func = self.find_compile_time_binary_operator(
- operand1, operand2)
- return func(operand1, operand2)
- except Exception as e:
- self.compile_time_value_error(e)
- def _check_truedivision(self, env):
- if self.cdivision or env.directives['cdivision']:
- self.ctruedivision = False
- else:
- self.ctruedivision = self.truedivision
- def infer_type(self, env):
- self._check_truedivision(env)
- return self.result_type(
- self.operand1.infer_type(env),
- self.operand2.infer_type(env), env)
- def analyse_operation(self, env):
- self._check_truedivision(env)
- NumBinopNode.analyse_operation(self, env)
- if self.is_cpp_operation():
- self.cdivision = True
- if not self.type.is_pyobject:
- self.zerodivision_check = (
- self.cdivision is None and not env.directives['cdivision']
- and (not self.operand2.has_constant_result() or
- self.operand2.constant_result == 0))
- if self.zerodivision_check or env.directives['cdivision_warnings']:
- # Need to check ahead of time to warn or raise zero division error
- self.operand1 = self.operand1.coerce_to_simple(env)
- self.operand2 = self.operand2.coerce_to_simple(env)
- def compute_c_result_type(self, type1, type2):
- if self.operator == '/' and self.ctruedivision and not type1.is_cpp_class and not type2.is_cpp_class:
- if not type1.is_float and not type2.is_float:
- widest_type = PyrexTypes.widest_numeric_type(type1, PyrexTypes.c_double_type)
- widest_type = PyrexTypes.widest_numeric_type(type2, widest_type)
- return widest_type
- return NumBinopNode.compute_c_result_type(self, type1, type2)
- def zero_division_message(self):
- if self.type.is_int:
- return "integer division or modulo by zero"
- else:
- return "float division"
- def generate_evaluation_code(self, code):
- if not self.type.is_pyobject and not self.type.is_complex:
- if self.cdivision is None:
- self.cdivision = (
- code.globalstate.directives['cdivision']
- or self.type.is_float
- or ((self.type.is_numeric or self.type.is_enum) and not self.type.signed)
- )
- if not self.cdivision:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("DivInt", "CMath.c").specialize(self.type))
- NumBinopNode.generate_evaluation_code(self, code)
- self.generate_div_warning_code(code)
- def generate_div_warning_code(self, code):
- in_nogil = self.in_nogil_context
- if not self.type.is_pyobject:
- if self.zerodivision_check:
- if not self.infix:
- zero_test = "%s(%s)" % (self.type.unary_op('zero'), self.operand2.result())
- else:
- zero_test = "%s == 0" % self.operand2.result()
- code.putln("if (unlikely(%s)) {" % zero_test)
- if in_nogil:
- code.put_ensure_gil()
- code.putln('PyErr_SetString(PyExc_ZeroDivisionError, "%s");' % self.zero_division_message())
- if in_nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- if self.type.is_int and self.type.signed and self.operator != '%':
- code.globalstate.use_utility_code(UtilityCode.load_cached("UnaryNegOverflows", "Overflow.c"))
- if self.operand2.type.signed == 2:
- # explicitly signed, no runtime check needed
- minus1_check = 'unlikely(%s == -1)' % self.operand2.result()
- else:
- type_of_op2 = self.operand2.type.empty_declaration_code()
- minus1_check = '(!(((%s)-1) > 0)) && unlikely(%s == (%s)-1)' % (
- type_of_op2, self.operand2.result(), type_of_op2)
- code.putln("else if (sizeof(%s) == sizeof(long) && %s "
- " && unlikely(UNARY_NEG_WOULD_OVERFLOW(%s))) {" % (
- self.type.empty_declaration_code(),
- minus1_check,
- self.operand1.result()))
- if in_nogil:
- code.put_ensure_gil()
- code.putln('PyErr_SetString(PyExc_OverflowError, "value too large to perform division");')
- if in_nogil:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- if code.globalstate.directives['cdivision_warnings'] and self.operator != '/':
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("CDivisionWarning", "CMath.c"))
- code.putln("if (unlikely((%s < 0) ^ (%s < 0))) {" % (
- self.operand1.result(),
- self.operand2.result()))
- warning_code = "__Pyx_cdivision_warning(%(FILENAME)s, %(LINENO)s)" % {
- 'FILENAME': Naming.filename_cname,
- 'LINENO': Naming.lineno_cname,
- }
- if in_nogil:
- result_code = 'result'
- code.putln("int %s;" % result_code)
- code.put_ensure_gil()
- code.putln(code.set_error_info(self.pos, used=True))
- code.putln("%s = %s;" % (result_code, warning_code))
- code.put_release_ensured_gil()
- else:
- result_code = warning_code
- code.putln(code.set_error_info(self.pos, used=True))
- code.put("if (unlikely(%s)) " % result_code)
- code.put_goto(code.error_label)
- code.putln("}")
- def calculate_result_code(self):
- if self.type.is_complex or self.is_cpp_operation():
- return NumBinopNode.calculate_result_code(self)
- elif self.type.is_float and self.operator == '//':
- return "floor(%s / %s)" % (
- self.operand1.result(),
- self.operand2.result())
- elif self.truedivision or self.cdivision:
- op1 = self.operand1.result()
- op2 = self.operand2.result()
- if self.truedivision:
- if self.type != self.operand1.type:
- op1 = self.type.cast_code(op1)
- if self.type != self.operand2.type:
- op2 = self.type.cast_code(op2)
- return "(%s / %s)" % (op1, op2)
- else:
- return "__Pyx_div_%s(%s, %s)" % (
- self.type.specialization_name(),
- self.operand1.result(),
- self.operand2.result())
- _find_formatting_types = re.compile(
- br"%"
- br"(?:%|" # %%
- br"(?:\([^)]+\))?" # %(name)
- br"[-+#,0-9 ]*([a-z])" # %.2f etc.
- br")").findall
- # These format conversion types can never trigger a Unicode string conversion in Py2.
- _safe_bytes_formats = set([
- # Excludes 's' and 'r', which can generate non-bytes strings.
- b'd', b'i', b'o', b'u', b'x', b'X', b'e', b'E', b'f', b'F', b'g', b'G', b'c', b'b', b'a',
- ])
- class ModNode(DivNode):
- # '%' operator.
- def is_py_operation_types(self, type1, type2):
- return (type1.is_string
- or type2.is_string
- or NumBinopNode.is_py_operation_types(self, type1, type2))
- def infer_builtin_types_operation(self, type1, type2):
- # b'%s' % xyz raises an exception in Py3<3.5, so it's safe to infer the type for Py2 and later Py3's.
- if type1 is unicode_type:
- # None + xyz may be implemented by RHS
- if type2.is_builtin_type or not self.operand1.may_be_none():
- return type1
- elif type1 in (bytes_type, str_type, basestring_type):
- if type2 is unicode_type:
- return type2
- elif type2.is_numeric:
- return type1
- elif self.operand1.is_string_literal:
- if type1 is str_type or type1 is bytes_type:
- if set(_find_formatting_types(self.operand1.value)) <= _safe_bytes_formats:
- return type1
- return basestring_type
- elif type1 is bytes_type and not type2.is_builtin_type:
- return None # RHS might implement '% operator differently in Py3
- else:
- return basestring_type # either str or unicode, can't tell
- return None
- def zero_division_message(self):
- if self.type.is_int:
- return "integer division or modulo by zero"
- else:
- return "float divmod()"
- def analyse_operation(self, env):
- DivNode.analyse_operation(self, env)
- if not self.type.is_pyobject:
- if self.cdivision is None:
- self.cdivision = env.directives['cdivision'] or not self.type.signed
- if not self.cdivision and not self.type.is_int and not self.type.is_float:
- error(self.pos, "mod operator not supported for type '%s'" % self.type)
- def generate_evaluation_code(self, code):
- if not self.type.is_pyobject and not self.cdivision:
- if self.type.is_int:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ModInt", "CMath.c").specialize(self.type))
- else: # float
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ModFloat", "CMath.c").specialize(
- self.type, math_h_modifier=self.type.math_h_modifier))
- # NOTE: skipping over DivNode here
- NumBinopNode.generate_evaluation_code(self, code)
- self.generate_div_warning_code(code)
- def calculate_result_code(self):
- if self.cdivision:
- if self.type.is_float:
- return "fmod%s(%s, %s)" % (
- self.type.math_h_modifier,
- self.operand1.result(),
- self.operand2.result())
- else:
- return "(%s %% %s)" % (
- self.operand1.result(),
- self.operand2.result())
- else:
- return "__Pyx_mod_%s(%s, %s)" % (
- self.type.specialization_name(),
- self.operand1.result(),
- self.operand2.result())
- def py_operation_function(self, code):
- type1, type2 = self.operand1.type, self.operand2.type
- # ("..." % x) must call "x.__rmod__()" for string subtypes.
- if type1 is unicode_type:
- if self.operand1.may_be_none() or (
- type2.is_extension_type and type2.subtype_of(type1) or
- type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
- return '__Pyx_PyUnicode_FormatSafe'
- else:
- return 'PyUnicode_Format'
- elif type1 is str_type:
- if self.operand1.may_be_none() or (
- type2.is_extension_type and type2.subtype_of(type1) or
- type2 is py_object_type and not isinstance(self.operand2, CoerceToPyTypeNode)):
- return '__Pyx_PyString_FormatSafe'
- else:
- return '__Pyx_PyString_Format'
- return super(ModNode, self).py_operation_function(code)
- class PowNode(NumBinopNode):
- # '**' operator.
- def analyse_c_operation(self, env):
- NumBinopNode.analyse_c_operation(self, env)
- if self.type.is_complex:
- if self.type.real_type.is_float:
- self.operand1 = self.operand1.coerce_to(self.type, env)
- self.operand2 = self.operand2.coerce_to(self.type, env)
- self.pow_func = self.type.binary_op('**')
- else:
- error(self.pos, "complex int powers not supported")
- self.pow_func = "<error>"
- elif self.type.is_float:
- self.pow_func = "pow" + self.type.math_h_modifier
- elif self.type.is_int:
- self.pow_func = "__Pyx_pow_%s" % self.type.empty_declaration_code().replace(' ', '_')
- env.use_utility_code(
- UtilityCode.load_cached("IntPow", "CMath.c").specialize(
- func_name=self.pow_func,
- type=self.type.empty_declaration_code(),
- signed=self.type.signed and 1 or 0))
- elif not self.type.is_error:
- error(self.pos, "got unexpected types for C power operator: %s, %s" %
- (self.operand1.type, self.operand2.type))
- def calculate_result_code(self):
- # Work around MSVC overloading ambiguity.
- def typecast(operand):
- if self.type == operand.type:
- return operand.result()
- else:
- return self.type.cast_code(operand.result())
- return "%s(%s, %s)" % (
- self.pow_func,
- typecast(self.operand1),
- typecast(self.operand2))
- def py_operation_function(self, code):
- if (self.type.is_pyobject and
- self.operand1.constant_result == 2 and
- isinstance(self.operand1.constant_result, _py_int_types) and
- self.operand2.type is py_object_type):
- code.globalstate.use_utility_code(UtilityCode.load_cached('PyNumberPow2', 'Optimize.c'))
- if self.inplace:
- return '__Pyx_PyNumber_InPlacePowerOf2'
- else:
- return '__Pyx_PyNumber_PowerOf2'
- return super(PowNode, self).py_operation_function(code)
- class BoolBinopNode(ExprNode):
- """
- Short-circuiting boolean operation.
- Note that this node provides the same code generation method as
- BoolBinopResultNode to simplify expression nesting.
- operator string "and"/"or"
- operand1 BoolBinopNode/BoolBinopResultNode left operand
- operand2 BoolBinopNode/BoolBinopResultNode right operand
- """
- subexprs = ['operand1', 'operand2']
- is_temp = True
- operator = None
- operand1 = None
- operand2 = None
- def infer_type(self, env):
- type1 = self.operand1.infer_type(env)
- type2 = self.operand2.infer_type(env)
- return PyrexTypes.independent_spanning_type(type1, type2)
- def may_be_none(self):
- if self.operator == 'or':
- return self.operand2.may_be_none()
- else:
- return self.operand1.may_be_none() or self.operand2.may_be_none()
- def calculate_constant_result(self):
- operand1 = self.operand1.constant_result
- operand2 = self.operand2.constant_result
- if self.operator == 'and':
- self.constant_result = operand1 and operand2
- else:
- self.constant_result = operand1 or operand2
- def compile_time_value(self, denv):
- operand1 = self.operand1.compile_time_value(denv)
- operand2 = self.operand2.compile_time_value(denv)
- if self.operator == 'and':
- return operand1 and operand2
- else:
- return operand1 or operand2
- def is_ephemeral(self):
- return self.operand1.is_ephemeral() or self.operand2.is_ephemeral()
- def analyse_types(self, env):
- # Note: we do not do any coercion here as we most likely do not know the final type anyway.
- # We even accept to set self.type to ErrorType if both operands do not have a spanning type.
- # The coercion to the final type and to a "simple" value is left to coerce_to().
- operand1 = self.operand1.analyse_types(env)
- operand2 = self.operand2.analyse_types(env)
- self.type = PyrexTypes.independent_spanning_type(
- operand1.type, operand2.type)
- self.operand1 = self._wrap_operand(operand1, env)
- self.operand2 = self._wrap_operand(operand2, env)
- return self
- def _wrap_operand(self, operand, env):
- if not isinstance(operand, (BoolBinopNode, BoolBinopResultNode)):
- operand = BoolBinopResultNode(operand, self.type, env)
- return operand
- def wrap_operands(self, env):
- """
- Must get called by transforms that want to create a correct BoolBinopNode
- after the type analysis phase.
- """
- self.operand1 = self._wrap_operand(self.operand1, env)
- self.operand2 = self._wrap_operand(self.operand2, env)
- def coerce_to_boolean(self, env):
- return self.coerce_to(PyrexTypes.c_bint_type, env)
- def coerce_to(self, dst_type, env):
- operand1 = self.operand1.coerce_to(dst_type, env)
- operand2 = self.operand2.coerce_to(dst_type, env)
- return BoolBinopNode.from_node(
- self, type=dst_type,
- operator=self.operator,
- operand1=operand1, operand2=operand2)
- def generate_bool_evaluation_code(self, code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through):
- code.mark_pos(self.pos)
- outer_labels = (and_label, or_label)
- if self.operator == 'and':
- my_label = and_label = code.new_label('next_and')
- else:
- my_label = or_label = code.new_label('next_or')
- self.operand1.generate_bool_evaluation_code(
- code, final_result_temp, final_result_type, and_label, or_label, end_label, my_label)
- and_label, or_label = outer_labels
- code.put_label(my_label)
- self.operand2.generate_bool_evaluation_code(
- code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through)
- def generate_evaluation_code(self, code):
- self.allocate_temp_result(code)
- result_type = PyrexTypes.py_object_type if self.type.is_pyobject else self.type
- or_label = and_label = None
- end_label = code.new_label('bool_binop_done')
- self.generate_bool_evaluation_code(code, self.result(), result_type, and_label, or_label, end_label, end_label)
- code.put_label(end_label)
- gil_message = "Truth-testing Python object"
- def check_const(self):
- return self.operand1.check_const() and self.operand2.check_const()
- def generate_subexpr_disposal_code(self, code):
- pass # nothing to do here, all done in generate_evaluation_code()
- def free_subexpr_temps(self, code):
- pass # nothing to do here, all done in generate_evaluation_code()
- def generate_operand1_test(self, code):
- # Generate code to test the truth of the first operand.
- if self.type.is_pyobject:
- test_result = code.funcstate.allocate_temp(
- PyrexTypes.c_bint_type, manage_ref=False)
- code.putln(
- "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
- test_result,
- self.operand1.py_result(),
- code.error_goto_if_neg(test_result, self.pos)))
- else:
- test_result = self.operand1.result()
- return (test_result, self.type.is_pyobject)
- class BoolBinopResultNode(ExprNode):
- """
- Intermediate result of a short-circuiting and/or expression.
- Tests the result for 'truthiness' and takes care of coercing the final result
- of the overall expression to the target type.
- Note that this node provides the same code generation method as
- BoolBinopNode to simplify expression nesting.
- arg ExprNode the argument to test
- value ExprNode the coerced result value node
- """
- subexprs = ['arg', 'value']
- is_temp = True
- arg = None
- value = None
- def __init__(self, arg, result_type, env):
- # using 'arg' multiple times, so it must be a simple/temp value
- arg = arg.coerce_to_simple(env)
- # wrap in ProxyNode, in case a transform wants to replace self.arg later
- arg = ProxyNode(arg)
- super(BoolBinopResultNode, self).__init__(
- arg.pos, arg=arg, type=result_type,
- value=CloneNode(arg).coerce_to(result_type, env))
- def coerce_to_boolean(self, env):
- return self.coerce_to(PyrexTypes.c_bint_type, env)
- def coerce_to(self, dst_type, env):
- # unwrap, coerce, rewrap
- arg = self.arg.arg
- if dst_type is PyrexTypes.c_bint_type:
- arg = arg.coerce_to_boolean(env)
- # TODO: unwrap more coercion nodes?
- return BoolBinopResultNode(arg, dst_type, env)
- def nogil_check(self, env):
- # let's leave all errors to BoolBinopNode
- pass
- def generate_operand_test(self, code):
- # Generate code to test the truth of the first operand.
- if self.arg.type.is_pyobject:
- test_result = code.funcstate.allocate_temp(
- PyrexTypes.c_bint_type, manage_ref=False)
- code.putln(
- "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
- test_result,
- self.arg.py_result(),
- code.error_goto_if_neg(test_result, self.pos)))
- else:
- test_result = self.arg.result()
- return (test_result, self.arg.type.is_pyobject)
- def generate_bool_evaluation_code(self, code, final_result_temp, final_result_type, and_label, or_label, end_label, fall_through):
- code.mark_pos(self.pos)
- # x => x
- # x and ... or ... => next 'and' / 'or'
- # False ... or x => next 'or'
- # True and x => next 'and'
- # True or x => True (operand)
- self.arg.generate_evaluation_code(code)
- if and_label or or_label:
- test_result, uses_temp = self.generate_operand_test(code)
- if uses_temp and (and_label and or_label):
- # cannot become final result => free early
- # disposal: uses_temp and (and_label and or_label)
- self.arg.generate_disposal_code(code)
- sense = '!' if or_label else ''
- code.putln("if (%s%s) {" % (sense, test_result))
- if uses_temp:
- code.funcstate.release_temp(test_result)
- if not uses_temp or not (and_label and or_label):
- # disposal: (not uses_temp) or {not (and_label and or_label) [if]}
- self.arg.generate_disposal_code(code)
- if or_label and or_label != fall_through:
- # value is false => short-circuit to next 'or'
- code.put_goto(or_label)
- if and_label:
- # value is true => go to next 'and'
- if or_label:
- code.putln("} else {")
- if not uses_temp:
- # disposal: (not uses_temp) and {(and_label and or_label) [else]}
- self.arg.generate_disposal_code(code)
- if and_label != fall_through:
- code.put_goto(and_label)
- if not and_label or not or_label:
- # if no next 'and' or 'or', we provide the result
- if and_label or or_label:
- code.putln("} else {")
- self.value.generate_evaluation_code(code)
- self.value.make_owned_reference(code)
- code.putln("%s = %s;" % (final_result_temp, self.value.result_as(final_result_type)))
- self.value.generate_post_assignment_code(code)
- # disposal: {not (and_label and or_label) [else]}
- self.arg.generate_disposal_code(code)
- self.value.free_temps(code)
- if end_label != fall_through:
- code.put_goto(end_label)
- if and_label or or_label:
- code.putln("}")
- self.arg.free_temps(code)
- class CondExprNode(ExprNode):
- # Short-circuiting conditional expression.
- #
- # test ExprNode
- # true_val ExprNode
- # false_val ExprNode
- true_val = None
- false_val = None
- is_temp = True
- subexprs = ['test', 'true_val', 'false_val']
- def type_dependencies(self, env):
- return self.true_val.type_dependencies(env) + self.false_val.type_dependencies(env)
- def infer_type(self, env):
- return PyrexTypes.independent_spanning_type(
- self.true_val.infer_type(env),
- self.false_val.infer_type(env))
- def calculate_constant_result(self):
- if self.test.constant_result:
- self.constant_result = self.true_val.constant_result
- else:
- self.constant_result = self.false_val.constant_result
- def is_ephemeral(self):
- return self.true_val.is_ephemeral() or self.false_val.is_ephemeral()
- def analyse_types(self, env):
- self.test = self.test.analyse_types(env).coerce_to_boolean(env)
- self.true_val = self.true_val.analyse_types(env)
- self.false_val = self.false_val.analyse_types(env)
- return self.analyse_result_type(env)
- def analyse_result_type(self, env):
- self.type = PyrexTypes.independent_spanning_type(
- self.true_val.type, self.false_val.type)
- if self.type.is_reference:
- self.type = PyrexTypes.CFakeReferenceType(self.type.ref_base_type)
- if self.type.is_pyobject:
- self.result_ctype = py_object_type
- elif self.true_val.is_ephemeral() or self.false_val.is_ephemeral():
- error(self.pos, "Unsafe C derivative of temporary Python reference used in conditional expression")
- if self.true_val.type.is_pyobject or self.false_val.type.is_pyobject:
- self.true_val = self.true_val.coerce_to(self.type, env)
- self.false_val = self.false_val.coerce_to(self.type, env)
- if self.type.is_error:
- self.type_error()
- return self
- def coerce_to_integer(self, env):
- self.true_val = self.true_val.coerce_to_integer(env)
- self.false_val = self.false_val.coerce_to_integer(env)
- self.result_ctype = None
- return self.analyse_result_type(env)
- def coerce_to(self, dst_type, env):
- self.true_val = self.true_val.coerce_to(dst_type, env)
- self.false_val = self.false_val.coerce_to(dst_type, env)
- self.result_ctype = None
- return self.analyse_result_type(env)
- def type_error(self):
- if not (self.true_val.type.is_error or self.false_val.type.is_error):
- error(self.pos, "Incompatible types in conditional expression (%s; %s)" %
- (self.true_val.type, self.false_val.type))
- self.type = PyrexTypes.error_type
- def check_const(self):
- return (self.test.check_const()
- and self.true_val.check_const()
- and self.false_val.check_const())
- def generate_evaluation_code(self, code):
- # Because subexprs may not be evaluated we can use a more optimal
- # subexpr allocation strategy than the default, so override evaluation_code.
- code.mark_pos(self.pos)
- self.allocate_temp_result(code)
- self.test.generate_evaluation_code(code)
- code.putln("if (%s) {" % self.test.result())
- self.eval_and_get(code, self.true_val)
- code.putln("} else {")
- self.eval_and_get(code, self.false_val)
- code.putln("}")
- self.test.generate_disposal_code(code)
- self.test.free_temps(code)
- def eval_and_get(self, code, expr):
- expr.generate_evaluation_code(code)
- if self.type.is_memoryviewslice:
- expr.make_owned_memoryviewslice(code)
- else:
- expr.make_owned_reference(code)
- code.putln('%s = %s;' % (self.result(), expr.result_as(self.ctype())))
- expr.generate_post_assignment_code(code)
- expr.free_temps(code)
- def generate_subexpr_disposal_code(self, code):
- pass # done explicitly above (cleanup must separately happen within the if/else blocks)
- def free_subexpr_temps(self, code):
- pass # done explicitly above (cleanup must separately happen within the if/else blocks)
- richcmp_constants = {
- "<" : "Py_LT",
- "<=": "Py_LE",
- "==": "Py_EQ",
- "!=": "Py_NE",
- "<>": "Py_NE",
- ">" : "Py_GT",
- ">=": "Py_GE",
- # the following are faked by special compare functions
- "in" : "Py_EQ",
- "not_in": "Py_NE",
- }
- class CmpNode(object):
- # Mixin class containing code common to PrimaryCmpNodes
- # and CascadedCmpNodes.
- special_bool_cmp_function = None
- special_bool_cmp_utility_code = None
- def infer_type(self, env):
- # TODO: Actually implement this (after merging with -unstable).
- return py_object_type
- def calculate_cascaded_constant_result(self, operand1_result):
- func = compile_time_binary_operators[self.operator]
- operand2_result = self.operand2.constant_result
- if (isinstance(operand1_result, any_string_type) and
- isinstance(operand2_result, any_string_type) and
- type(operand1_result) != type(operand2_result)):
- # string comparison of different types isn't portable
- return
- if self.operator in ('in', 'not_in'):
- if isinstance(self.operand2, (ListNode, TupleNode, SetNode)):
- if not self.operand2.args:
- self.constant_result = self.operator == 'not_in'
- return
- elif isinstance(self.operand2, ListNode) and not self.cascade:
- # tuples are more efficient to store than lists
- self.operand2 = self.operand2.as_tuple()
- elif isinstance(self.operand2, DictNode):
- if not self.operand2.key_value_pairs:
- self.constant_result = self.operator == 'not_in'
- return
- self.constant_result = func(operand1_result, operand2_result)
- def cascaded_compile_time_value(self, operand1, denv):
- func = get_compile_time_binop(self)
- operand2 = self.operand2.compile_time_value(denv)
- try:
- result = func(operand1, operand2)
- except Exception as e:
- self.compile_time_value_error(e)
- result = None
- if result:
- cascade = self.cascade
- if cascade:
- result = result and cascade.cascaded_compile_time_value(operand2, denv)
- return result
- def is_cpp_comparison(self):
- return self.operand1.type.is_cpp_class or self.operand2.type.is_cpp_class
- def find_common_int_type(self, env, op, operand1, operand2):
- # type1 != type2 and at least one of the types is not a C int
- type1 = operand1.type
- type2 = operand2.type
- type1_can_be_int = False
- type2_can_be_int = False
- if operand1.is_string_literal and operand1.can_coerce_to_char_literal():
- type1_can_be_int = True
- if operand2.is_string_literal and operand2.can_coerce_to_char_literal():
- type2_can_be_int = True
- if type1.is_int:
- if type2_can_be_int:
- return type1
- elif type2.is_int:
- if type1_can_be_int:
- return type2
- elif type1_can_be_int:
- if type2_can_be_int:
- if Builtin.unicode_type in (type1, type2):
- return PyrexTypes.c_py_ucs4_type
- else:
- return PyrexTypes.c_uchar_type
- return None
- def find_common_type(self, env, op, operand1, common_type=None):
- operand2 = self.operand2
- type1 = operand1.type
- type2 = operand2.type
- new_common_type = None
- # catch general errors
- if (type1 == str_type and (type2.is_string or type2 in (bytes_type, unicode_type)) or
- type2 == str_type and (type1.is_string or type1 in (bytes_type, unicode_type))):
- error(self.pos, "Comparisons between bytes/unicode and str are not portable to Python 3")
- new_common_type = error_type
- # try to use numeric comparisons where possible
- elif type1.is_complex or type2.is_complex:
- if (op not in ('==', '!=')
- and (type1.is_complex or type1.is_numeric)
- and (type2.is_complex or type2.is_numeric)):
- error(self.pos, "complex types are unordered")
- new_common_type = error_type
- elif type1.is_pyobject:
- new_common_type = Builtin.complex_type if type1.subtype_of(Builtin.complex_type) else py_object_type
- elif type2.is_pyobject:
- new_common_type = Builtin.complex_type if type2.subtype_of(Builtin.complex_type) else py_object_type
- else:
- new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
- elif type1.is_numeric and type2.is_numeric:
- new_common_type = PyrexTypes.widest_numeric_type(type1, type2)
- elif common_type is None or not common_type.is_pyobject:
- new_common_type = self.find_common_int_type(env, op, operand1, operand2)
- if new_common_type is None:
- # fall back to generic type compatibility tests
- if type1.is_ctuple or type2.is_ctuple:
- new_common_type = py_object_type
- elif type1 == type2:
- new_common_type = type1
- elif type1.is_pyobject or type2.is_pyobject:
- if type2.is_numeric or type2.is_string:
- if operand2.check_for_coercion_error(type1, env):
- new_common_type = error_type
- else:
- new_common_type = py_object_type
- elif type1.is_numeric or type1.is_string:
- if operand1.check_for_coercion_error(type2, env):
- new_common_type = error_type
- else:
- new_common_type = py_object_type
- elif py_object_type.assignable_from(type1) and py_object_type.assignable_from(type2):
- new_common_type = py_object_type
- else:
- # one Python type and one non-Python type, not assignable
- self.invalid_types_error(operand1, op, operand2)
- new_common_type = error_type
- elif type1.assignable_from(type2):
- new_common_type = type1
- elif type2.assignable_from(type1):
- new_common_type = type2
- else:
- # C types that we couldn't handle up to here are an error
- self.invalid_types_error(operand1, op, operand2)
- new_common_type = error_type
- if new_common_type.is_string and (isinstance(operand1, BytesNode) or
- isinstance(operand2, BytesNode)):
- # special case when comparing char* to bytes literal: must
- # compare string values!
- new_common_type = bytes_type
- # recursively merge types
- if common_type is None or new_common_type.is_error:
- common_type = new_common_type
- else:
- # we could do a lot better by splitting the comparison
- # into a non-Python part and a Python part, but this is
- # safer for now
- common_type = PyrexTypes.spanning_type(common_type, new_common_type)
- if self.cascade:
- common_type = self.cascade.find_common_type(env, self.operator, operand2, common_type)
- return common_type
- def invalid_types_error(self, operand1, op, operand2):
- error(self.pos, "Invalid types for '%s' (%s, %s)" %
- (op, operand1.type, operand2.type))
- def is_python_comparison(self):
- return (not self.is_ptr_contains()
- and not self.is_c_string_contains()
- and (self.has_python_operands()
- or (self.cascade and self.cascade.is_python_comparison())
- or self.operator in ('in', 'not_in')))
- def coerce_operands_to(self, dst_type, env):
- operand2 = self.operand2
- if operand2.type != dst_type:
- self.operand2 = operand2.coerce_to(dst_type, env)
- if self.cascade:
- self.cascade.coerce_operands_to(dst_type, env)
- def is_python_result(self):
- return ((self.has_python_operands() and
- self.special_bool_cmp_function is None and
- self.operator not in ('is', 'is_not', 'in', 'not_in') and
- not self.is_c_string_contains() and
- not self.is_ptr_contains())
- or (self.cascade and self.cascade.is_python_result()))
- def is_c_string_contains(self):
- return self.operator in ('in', 'not_in') and \
- ((self.operand1.type.is_int
- and (self.operand2.type.is_string or self.operand2.type is bytes_type)) or
- (self.operand1.type.is_unicode_char
- and self.operand2.type is unicode_type))
- def is_ptr_contains(self):
- if self.operator in ('in', 'not_in'):
- container_type = self.operand2.type
- return (container_type.is_ptr or container_type.is_array) \
- and not container_type.is_string
- def find_special_bool_compare_function(self, env, operand1, result_is_bool=False):
- # note: currently operand1 must get coerced to a Python object if we succeed here!
- if self.operator in ('==', '!='):
- type1, type2 = operand1.type, self.operand2.type
- if result_is_bool or (type1.is_builtin_type and type2.is_builtin_type):
- if type1 is Builtin.unicode_type or type2 is Builtin.unicode_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("UnicodeEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyUnicode_Equals"
- return True
- elif type1 is Builtin.bytes_type or type2 is Builtin.bytes_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("BytesEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyBytes_Equals"
- return True
- elif type1 is Builtin.basestring_type or type2 is Builtin.basestring_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("UnicodeEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyUnicode_Equals"
- return True
- elif type1 is Builtin.str_type or type2 is Builtin.str_type:
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("StrEquals", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyString_Equals"
- return True
- elif self.operator in ('in', 'not_in'):
- if self.operand2.type is Builtin.dict_type:
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyDictContains", "ObjectHandling.c")
- self.special_bool_cmp_function = "__Pyx_PyDict_ContainsTF"
- return True
- elif self.operand2.type is Builtin.set_type:
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PySetContains", "ObjectHandling.c")
- self.special_bool_cmp_function = "__Pyx_PySet_ContainsTF"
- return True
- elif self.operand2.type is Builtin.unicode_type:
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyUnicodeContains", "StringTools.c")
- self.special_bool_cmp_function = "__Pyx_PyUnicode_ContainsTF"
- return True
- else:
- if not self.operand2.type.is_pyobject:
- self.operand2 = self.operand2.coerce_to_pyobject(env)
- self.special_bool_cmp_utility_code = UtilityCode.load_cached("PySequenceContains", "ObjectHandling.c")
- self.special_bool_cmp_function = "__Pyx_PySequence_ContainsTF"
- return True
- return False
- def generate_operation_code(self, code, result_code,
- operand1, op , operand2):
- if self.type.is_pyobject:
- error_clause = code.error_goto_if_null
- got_ref = "__Pyx_XGOTREF(%s); " % result_code
- if self.special_bool_cmp_function:
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("PyBoolOrNullFromLong", "ObjectHandling.c"))
- coerce_result = "__Pyx_PyBoolOrNull_FromLong"
- else:
- coerce_result = "__Pyx_PyBool_FromLong"
- else:
- error_clause = code.error_goto_if_neg
- got_ref = ""
- coerce_result = ""
- if self.special_bool_cmp_function:
- if operand1.type.is_pyobject:
- result1 = operand1.py_result()
- else:
- result1 = operand1.result()
- if operand2.type.is_pyobject:
- result2 = operand2.py_result()
- else:
- result2 = operand2.result()
- if self.special_bool_cmp_utility_code:
- code.globalstate.use_utility_code(self.special_bool_cmp_utility_code)
- code.putln(
- "%s = %s(%s(%s, %s, %s)); %s%s" % (
- result_code,
- coerce_result,
- self.special_bool_cmp_function,
- result1, result2, richcmp_constants[op],
- got_ref,
- error_clause(result_code, self.pos)))
- elif operand1.type.is_pyobject and op not in ('is', 'is_not'):
- assert op not in ('in', 'not_in'), op
- code.putln("%s = PyObject_RichCompare(%s, %s, %s); %s%s" % (
- result_code,
- operand1.py_result(),
- operand2.py_result(),
- richcmp_constants[op],
- got_ref,
- error_clause(result_code, self.pos)))
- elif operand1.type.is_complex:
- code.putln("%s = %s(%s%s(%s, %s));" % (
- result_code,
- coerce_result,
- op == "!=" and "!" or "",
- operand1.type.unary_op('eq'),
- operand1.result(),
- operand2.result()))
- else:
- type1 = operand1.type
- type2 = operand2.type
- if (type1.is_extension_type or type2.is_extension_type) \
- and not type1.same_as(type2):
- common_type = py_object_type
- elif type1.is_numeric:
- common_type = PyrexTypes.widest_numeric_type(type1, type2)
- else:
- common_type = type1
- code1 = operand1.result_as(common_type)
- code2 = operand2.result_as(common_type)
- statement = "%s = %s(%s %s %s);" % (
- result_code,
- coerce_result,
- code1,
- self.c_operator(op),
- code2)
- if self.is_cpp_comparison() and self.exception_check == '+':
- translate_cpp_exception(
- code,
- self.pos,
- statement,
- result_code if self.type.is_pyobject else None,
- self.exception_value,
- self.in_nogil_context)
- else:
- code.putln(statement)
- def c_operator(self, op):
- if op == 'is':
- return "=="
- elif op == 'is_not':
- return "!="
- else:
- return op
- class PrimaryCmpNode(ExprNode, CmpNode):
- # Non-cascaded comparison or first comparison of
- # a cascaded sequence.
- #
- # operator string
- # operand1 ExprNode
- # operand2 ExprNode
- # cascade CascadedCmpNode
- # We don't use the subexprs mechanism, because
- # things here are too complicated for it to handle.
- # Instead, we override all the framework methods
- # which use it.
- child_attrs = ['operand1', 'operand2', 'coerced_operand2', 'cascade']
- cascade = None
- coerced_operand2 = None
- is_memslice_nonecheck = False
- def infer_type(self, env):
- type1 = self.operand1.infer_type(env)
- type2 = self.operand2.infer_type(env)
- if is_pythran_expr(type1) or is_pythran_expr(type2):
- if is_pythran_supported_type(type1) and is_pythran_supported_type(type2):
- return PythranExpr(pythran_binop_type(self.operator, type1, type2))
- # TODO: implement this for other types.
- return py_object_type
- def type_dependencies(self, env):
- return ()
- def calculate_constant_result(self):
- assert not self.cascade
- self.calculate_cascaded_constant_result(self.operand1.constant_result)
- def compile_time_value(self, denv):
- operand1 = self.operand1.compile_time_value(denv)
- return self.cascaded_compile_time_value(operand1, denv)
- def analyse_types(self, env):
- self.operand1 = self.operand1.analyse_types(env)
- self.operand2 = self.operand2.analyse_types(env)
- if self.is_cpp_comparison():
- self.analyse_cpp_comparison(env)
- if self.cascade:
- error(self.pos, "Cascading comparison not yet supported for cpp types.")
- return self
- type1 = self.operand1.type
- type2 = self.operand2.type
- if is_pythran_expr(type1) or is_pythran_expr(type2):
- if is_pythran_supported_type(type1) and is_pythran_supported_type(type2):
- self.type = PythranExpr(pythran_binop_type(self.operator, type1, type2))
- self.is_pycmp = False
- return self
- if self.analyse_memoryviewslice_comparison(env):
- return self
- if self.cascade:
- self.cascade = self.cascade.analyse_types(env)
- if self.operator in ('in', 'not_in'):
- if self.is_c_string_contains():
- self.is_pycmp = False
- common_type = None
- if self.cascade:
- error(self.pos, "Cascading comparison not yet supported for 'int_val in string'.")
- return self
- if self.operand2.type is unicode_type:
- env.use_utility_code(UtilityCode.load_cached("PyUCS4InUnicode", "StringTools.c"))
- else:
- if self.operand1.type is PyrexTypes.c_uchar_type:
- self.operand1 = self.operand1.coerce_to(PyrexTypes.c_char_type, env)
- if self.operand2.type is not bytes_type:
- self.operand2 = self.operand2.coerce_to(bytes_type, env)
- env.use_utility_code(UtilityCode.load_cached("BytesContains", "StringTools.c"))
- self.operand2 = self.operand2.as_none_safe_node(
- "argument of type 'NoneType' is not iterable")
- elif self.is_ptr_contains():
- if self.cascade:
- error(self.pos, "Cascading comparison not supported for 'val in sliced pointer'.")
- self.type = PyrexTypes.c_bint_type
- # Will be transformed by IterationTransform
- return self
- elif self.find_special_bool_compare_function(env, self.operand1):
- if not self.operand1.type.is_pyobject:
- self.operand1 = self.operand1.coerce_to_pyobject(env)
- common_type = None # if coercion needed, the method call above has already done it
- self.is_pycmp = False # result is bint
- else:
- common_type = py_object_type
- self.is_pycmp = True
- elif self.find_special_bool_compare_function(env, self.operand1):
- if not self.operand1.type.is_pyobject:
- self.operand1 = self.operand1.coerce_to_pyobject(env)
- common_type = None # if coercion needed, the method call above has already done it
- self.is_pycmp = False # result is bint
- else:
- common_type = self.find_common_type(env, self.operator, self.operand1)
- self.is_pycmp = common_type.is_pyobject
- if common_type is not None and not common_type.is_error:
- if self.operand1.type != common_type:
- self.operand1 = self.operand1.coerce_to(common_type, env)
- self.coerce_operands_to(common_type, env)
- if self.cascade:
- self.operand2 = self.operand2.coerce_to_simple(env)
- self.cascade.coerce_cascaded_operands_to_temp(env)
- operand2 = self.cascade.optimise_comparison(self.operand2, env)
- if operand2 is not self.operand2:
- self.coerced_operand2 = operand2
- if self.is_python_result():
- self.type = PyrexTypes.py_object_type
- else:
- self.type = PyrexTypes.c_bint_type
- cdr = self.cascade
- while cdr:
- cdr.type = self.type
- cdr = cdr.cascade
- if self.is_pycmp or self.cascade or self.special_bool_cmp_function:
- # 1) owned reference, 2) reused value, 3) potential function error return value
- self.is_temp = 1
- return self
- def analyse_cpp_comparison(self, env):
- type1 = self.operand1.type
- type2 = self.operand2.type
- self.is_pycmp = False
- entry = env.lookup_operator(self.operator, [self.operand1, self.operand2])
- if entry is None:
- error(self.pos, "Invalid types for '%s' (%s, %s)" %
- (self.operator, type1, type2))
- self.type = PyrexTypes.error_type
- self.result_code = "<error>"
- return
- func_type = entry.type
- if func_type.is_ptr:
- func_type = func_type.base_type
- self.exception_check = func_type.exception_check
- self.exception_value = func_type.exception_value
- if self.exception_check == '+':
- self.is_temp = True
- if self.exception_value is None:
- env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
- if len(func_type.args) == 1:
- self.operand2 = self.operand2.coerce_to(func_type.args[0].type, env)
- else:
- self.operand1 = self.operand1.coerce_to(func_type.args[0].type, env)
- self.operand2 = self.operand2.coerce_to(func_type.args[1].type, env)
- self.type = func_type.return_type
- def analyse_memoryviewslice_comparison(self, env):
- have_none = self.operand1.is_none or self.operand2.is_none
- have_slice = (self.operand1.type.is_memoryviewslice or
- self.operand2.type.is_memoryviewslice)
- ops = ('==', '!=', 'is', 'is_not')
- if have_slice and have_none and self.operator in ops:
- self.is_pycmp = False
- self.type = PyrexTypes.c_bint_type
- self.is_memslice_nonecheck = True
- return True
- return False
- def coerce_to_boolean(self, env):
- if self.is_pycmp:
- # coercing to bool => may allow for more efficient comparison code
- if self.find_special_bool_compare_function(
- env, self.operand1, result_is_bool=True):
- self.is_pycmp = False
- self.type = PyrexTypes.c_bint_type
- self.is_temp = 1
- if self.cascade:
- operand2 = self.cascade.optimise_comparison(
- self.operand2, env, result_is_bool=True)
- if operand2 is not self.operand2:
- self.coerced_operand2 = operand2
- return self
- # TODO: check if we can optimise parts of the cascade here
- return ExprNode.coerce_to_boolean(self, env)
- def has_python_operands(self):
- return (self.operand1.type.is_pyobject
- or self.operand2.type.is_pyobject)
- def check_const(self):
- if self.cascade:
- self.not_const()
- return False
- else:
- return self.operand1.check_const() and self.operand2.check_const()
- def calculate_result_code(self):
- operand1, operand2 = self.operand1, self.operand2
- if operand1.type.is_complex:
- if self.operator == "!=":
- negation = "!"
- else:
- negation = ""
- return "(%s%s(%s, %s))" % (
- negation,
- operand1.type.binary_op('=='),
- operand1.result(),
- operand2.result())
- elif self.is_c_string_contains():
- if operand2.type is unicode_type:
- method = "__Pyx_UnicodeContainsUCS4"
- else:
- method = "__Pyx_BytesContains"
- if self.operator == "not_in":
- negation = "!"
- else:
- negation = ""
- return "(%s%s(%s, %s))" % (
- negation,
- method,
- operand2.result(),
- operand1.result())
- else:
- if is_pythran_expr(self.type):
- result1, result2 = operand1.pythran_result(), operand2.pythran_result()
- else:
- result1, result2 = operand1.result(), operand2.result()
- if self.is_memslice_nonecheck:
- if operand1.type.is_memoryviewslice:
- result1 = "((PyObject *) %s.memview)" % result1
- else:
- result2 = "((PyObject *) %s.memview)" % result2
- return "(%s %s %s)" % (
- result1,
- self.c_operator(self.operator),
- result2)
- def generate_evaluation_code(self, code):
- self.operand1.generate_evaluation_code(code)
- self.operand2.generate_evaluation_code(code)
- if self.is_temp:
- self.allocate_temp_result(code)
- self.generate_operation_code(code, self.result(),
- self.operand1, self.operator, self.operand2)
- if self.cascade:
- self.cascade.generate_evaluation_code(
- code, self.result(), self.coerced_operand2 or self.operand2,
- needs_evaluation=self.coerced_operand2 is not None)
- self.operand1.generate_disposal_code(code)
- self.operand1.free_temps(code)
- self.operand2.generate_disposal_code(code)
- self.operand2.free_temps(code)
- def generate_subexpr_disposal_code(self, code):
- # If this is called, it is a non-cascaded cmp,
- # so only need to dispose of the two main operands.
- self.operand1.generate_disposal_code(code)
- self.operand2.generate_disposal_code(code)
- def free_subexpr_temps(self, code):
- # If this is called, it is a non-cascaded cmp,
- # so only need to dispose of the two main operands.
- self.operand1.free_temps(code)
- self.operand2.free_temps(code)
- def annotate(self, code):
- self.operand1.annotate(code)
- self.operand2.annotate(code)
- if self.cascade:
- self.cascade.annotate(code)
- class CascadedCmpNode(Node, CmpNode):
- # A CascadedCmpNode is not a complete expression node. It
- # hangs off the side of another comparison node, shares
- # its left operand with that node, and shares its result
- # with the PrimaryCmpNode at the head of the chain.
- #
- # operator string
- # operand2 ExprNode
- # cascade CascadedCmpNode
- child_attrs = ['operand2', 'coerced_operand2', 'cascade']
- cascade = None
- coerced_operand2 = None
- constant_result = constant_value_not_set # FIXME: where to calculate this?
- def infer_type(self, env):
- # TODO: Actually implement this (after merging with -unstable).
- return py_object_type
- def type_dependencies(self, env):
- return ()
- def has_constant_result(self):
- return self.constant_result is not constant_value_not_set and \
- self.constant_result is not not_a_constant
- def analyse_types(self, env):
- self.operand2 = self.operand2.analyse_types(env)
- if self.cascade:
- self.cascade = self.cascade.analyse_types(env)
- return self
- def has_python_operands(self):
- return self.operand2.type.is_pyobject
- def is_cpp_comparison(self):
- # cascaded comparisons aren't currently implemented for c++ classes.
- return False
- def optimise_comparison(self, operand1, env, result_is_bool=False):
- if self.find_special_bool_compare_function(env, operand1, result_is_bool):
- self.is_pycmp = False
- self.type = PyrexTypes.c_bint_type
- if not operand1.type.is_pyobject:
- operand1 = operand1.coerce_to_pyobject(env)
- if self.cascade:
- operand2 = self.cascade.optimise_comparison(self.operand2, env, result_is_bool)
- if operand2 is not self.operand2:
- self.coerced_operand2 = operand2
- return operand1
- def coerce_operands_to_pyobjects(self, env):
- self.operand2 = self.operand2.coerce_to_pyobject(env)
- if self.operand2.type is dict_type and self.operator in ('in', 'not_in'):
- self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
- if self.cascade:
- self.cascade.coerce_operands_to_pyobjects(env)
- def coerce_cascaded_operands_to_temp(self, env):
- if self.cascade:
- #self.operand2 = self.operand2.coerce_to_temp(env) #CTT
- self.operand2 = self.operand2.coerce_to_simple(env)
- self.cascade.coerce_cascaded_operands_to_temp(env)
- def generate_evaluation_code(self, code, result, operand1, needs_evaluation=False):
- if self.type.is_pyobject:
- code.putln("if (__Pyx_PyObject_IsTrue(%s)) {" % result)
- code.put_decref(result, self.type)
- else:
- code.putln("if (%s) {" % result)
- if needs_evaluation:
- operand1.generate_evaluation_code(code)
- self.operand2.generate_evaluation_code(code)
- self.generate_operation_code(code, result,
- operand1, self.operator, self.operand2)
- if self.cascade:
- self.cascade.generate_evaluation_code(
- code, result, self.coerced_operand2 or self.operand2,
- needs_evaluation=self.coerced_operand2 is not None)
- if needs_evaluation:
- operand1.generate_disposal_code(code)
- operand1.free_temps(code)
- # Cascaded cmp result is always temp
- self.operand2.generate_disposal_code(code)
- self.operand2.free_temps(code)
- code.putln("}")
- def annotate(self, code):
- self.operand2.annotate(code)
- if self.cascade:
- self.cascade.annotate(code)
- binop_node_classes = {
- "or": BoolBinopNode,
- "and": BoolBinopNode,
- "|": IntBinopNode,
- "^": IntBinopNode,
- "&": IntBinopNode,
- "<<": IntBinopNode,
- ">>": IntBinopNode,
- "+": AddNode,
- "-": SubNode,
- "*": MulNode,
- "@": MatMultNode,
- "/": DivNode,
- "//": DivNode,
- "%": ModNode,
- "**": PowNode,
- }
- def binop_node(pos, operator, operand1, operand2, inplace=False, **kwargs):
- # Construct binop node of appropriate class for
- # given operator.
- return binop_node_classes[operator](
- pos,
- operator=operator,
- operand1=operand1,
- operand2=operand2,
- inplace=inplace,
- **kwargs)
- #-------------------------------------------------------------------
- #
- # Coercion nodes
- #
- # Coercion nodes are special in that they are created during
- # the analyse_types phase of parse tree processing.
- # Their __init__ methods consequently incorporate some aspects
- # of that phase.
- #
- #-------------------------------------------------------------------
- class CoercionNode(ExprNode):
- # Abstract base class for coercion nodes.
- #
- # arg ExprNode node being coerced
- subexprs = ['arg']
- constant_result = not_a_constant
- def __init__(self, arg):
- super(CoercionNode, self).__init__(arg.pos)
- self.arg = arg
- if debug_coercion:
- print("%s Coercing %s" % (self, self.arg))
- def calculate_constant_result(self):
- # constant folding can break type coercion, so this is disabled
- pass
- def annotate(self, code):
- self.arg.annotate(code)
- if self.arg.type != self.type:
- file, line, col = self.pos
- code.annotate((file, line, col-1), AnnotationItem(
- style='coerce', tag='coerce', text='[%s] to [%s]' % (self.arg.type, self.type)))
- class CoerceToMemViewSliceNode(CoercionNode):
- """
- Coerce an object to a memoryview slice. This holds a new reference in
- a managed temp.
- """
- def __init__(self, arg, dst_type, env):
- assert dst_type.is_memoryviewslice
- assert not arg.type.is_memoryviewslice
- CoercionNode.__init__(self, arg)
- self.type = dst_type
- self.is_temp = 1
- self.use_managed_ref = True
- self.arg = arg
- self.type.create_from_py_utility_code(env)
- def generate_result_code(self, code):
- code.putln(self.type.from_py_call_code(
- self.arg.py_result(),
- self.result(),
- self.pos,
- code
- ))
- class CastNode(CoercionNode):
- # Wrap a node in a C type cast.
- def __init__(self, arg, new_type):
- CoercionNode.__init__(self, arg)
- self.type = new_type
- def may_be_none(self):
- return self.arg.may_be_none()
- def calculate_result_code(self):
- return self.arg.result_as(self.type)
- def generate_result_code(self, code):
- self.arg.generate_result_code(code)
- class PyTypeTestNode(CoercionNode):
- # This node is used to check that a generic Python
- # object is an instance of a particular extension type.
- # This node borrows the result of its argument node.
- exact_builtin_type = True
- def __init__(self, arg, dst_type, env, notnone=False):
- # The arg is know to be a Python object, and
- # the dst_type is known to be an extension type.
- assert dst_type.is_extension_type or dst_type.is_builtin_type, "PyTypeTest on non extension type"
- CoercionNode.__init__(self, arg)
- self.type = dst_type
- self.result_ctype = arg.ctype()
- self.notnone = notnone
- nogil_check = Node.gil_error
- gil_message = "Python type test"
- def analyse_types(self, env):
- return self
- def may_be_none(self):
- if self.notnone:
- return False
- return self.arg.may_be_none()
- def is_simple(self):
- return self.arg.is_simple()
- def result_in_temp(self):
- return self.arg.result_in_temp()
- def is_ephemeral(self):
- return self.arg.is_ephemeral()
- def nonlocally_immutable(self):
- return self.arg.nonlocally_immutable()
- def reanalyse(self):
- if self.type != self.arg.type or not self.arg.is_temp:
- return self
- if not self.type.typeobj_is_available():
- return self
- if self.arg.may_be_none() and self.notnone:
- return self.arg.as_none_safe_node("Cannot convert NoneType to %.200s" % self.type.name)
- return self.arg
- def calculate_constant_result(self):
- # FIXME
- pass
- def calculate_result_code(self):
- return self.arg.result()
- def generate_result_code(self, code):
- if self.type.typeobj_is_available():
- if self.type.is_builtin_type:
- type_test = self.type.type_test_code(
- self.arg.py_result(),
- self.notnone, exact=self.exact_builtin_type)
- else:
- type_test = self.type.type_test_code(
- self.arg.py_result(), self.notnone)
- code.globalstate.use_utility_code(
- UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c"))
- code.putln("if (!(%s)) %s" % (
- type_test, code.error_goto(self.pos)))
- else:
- error(self.pos, "Cannot test type of extern C class "
- "without type object name specification")
- def generate_post_assignment_code(self, code):
- self.arg.generate_post_assignment_code(code)
- def allocate_temp_result(self, code):
- pass
- def release_temp_result(self, code):
- pass
- def free_temps(self, code):
- self.arg.free_temps(code)
- def free_subexpr_temps(self, code):
- self.arg.free_subexpr_temps(code)
- class NoneCheckNode(CoercionNode):
- # This node is used to check that a Python object is not None and
- # raises an appropriate exception (as specified by the creating
- # transform).
- is_nonecheck = True
- def __init__(self, arg, exception_type_cname, exception_message,
- exception_format_args=()):
- CoercionNode.__init__(self, arg)
- self.type = arg.type
- self.result_ctype = arg.ctype()
- self.exception_type_cname = exception_type_cname
- self.exception_message = exception_message
- self.exception_format_args = tuple(exception_format_args or ())
- nogil_check = None # this node only guards an operation that would fail already
- def analyse_types(self, env):
- return self
- def may_be_none(self):
- return False
- def is_simple(self):
- return self.arg.is_simple()
- def result_in_temp(self):
- return self.arg.result_in_temp()
- def nonlocally_immutable(self):
- return self.arg.nonlocally_immutable()
- def calculate_result_code(self):
- return self.arg.result()
- def condition(self):
- if self.type.is_pyobject:
- return self.arg.py_result()
- elif self.type.is_memoryviewslice:
- return "((PyObject *) %s.memview)" % self.arg.result()
- else:
- raise Exception("unsupported type")
- @classmethod
- def generate(cls, arg, code, exception_message,
- exception_type_cname="PyExc_TypeError", exception_format_args=(), in_nogil_context=False):
- node = cls(arg, exception_type_cname, exception_message, exception_format_args)
- node.in_nogil_context = in_nogil_context
- node.put_nonecheck(code)
- @classmethod
- def generate_if_needed(cls, arg, code, exception_message,
- exception_type_cname="PyExc_TypeError", exception_format_args=(), in_nogil_context=False):
- if arg.may_be_none():
- cls.generate(arg, code, exception_message, exception_type_cname, exception_format_args, in_nogil_context)
- def put_nonecheck(self, code):
- code.putln(
- "if (unlikely(%s == Py_None)) {" % self.condition())
- if self.in_nogil_context:
- code.put_ensure_gil()
- escape = StringEncoding.escape_byte_string
- if self.exception_format_args:
- code.putln('PyErr_Format(%s, "%s", %s);' % (
- self.exception_type_cname,
- StringEncoding.escape_byte_string(
- self.exception_message.encode('UTF-8')),
- ', '.join([ '"%s"' % escape(str(arg).encode('UTF-8'))
- for arg in self.exception_format_args ])))
- else:
- code.putln('PyErr_SetString(%s, "%s");' % (
- self.exception_type_cname,
- escape(self.exception_message.encode('UTF-8'))))
- if self.in_nogil_context:
- code.put_release_ensured_gil()
- code.putln(code.error_goto(self.pos))
- code.putln("}")
- def generate_result_code(self, code):
- self.put_nonecheck(code)
- def generate_post_assignment_code(self, code):
- self.arg.generate_post_assignment_code(code)
- def free_temps(self, code):
- self.arg.free_temps(code)
- class CoerceToPyTypeNode(CoercionNode):
- # This node is used to convert a C data type
- # to a Python object.
- type = py_object_type
- target_type = py_object_type
- is_temp = 1
- def __init__(self, arg, env, type=py_object_type):
- if not arg.type.create_to_py_utility_code(env):
- error(arg.pos, "Cannot convert '%s' to Python object" % arg.type)
- elif arg.type.is_complex:
- # special case: complex coercion is so complex that it
- # uses a macro ("__pyx_PyComplex_FromComplex()"), for
- # which the argument must be simple
- arg = arg.coerce_to_simple(env)
- CoercionNode.__init__(self, arg)
- if type is py_object_type:
- # be specific about some known types
- if arg.type.is_string or arg.type.is_cpp_string:
- self.type = default_str_type(env)
- elif arg.type.is_pyunicode_ptr or arg.type.is_unicode_char:
- self.type = unicode_type
- elif arg.type.is_complex:
- self.type = Builtin.complex_type
- self.target_type = self.type
- elif arg.type.is_string or arg.type.is_cpp_string:
- if (type not in (bytes_type, bytearray_type)
- and not env.directives['c_string_encoding']):
- error(arg.pos,
- "default encoding required for conversion from '%s' to '%s'" %
- (arg.type, type))
- self.type = self.target_type = type
- else:
- # FIXME: check that the target type and the resulting type are compatible
- self.target_type = type
- gil_message = "Converting to Python object"
- def may_be_none(self):
- # FIXME: is this always safe?
- return False
- def coerce_to_boolean(self, env):
- arg_type = self.arg.type
- if (arg_type == PyrexTypes.c_bint_type or
- (arg_type.is_pyobject and arg_type.name == 'bool')):
- return self.arg.coerce_to_temp(env)
- else:
- return CoerceToBooleanNode(self, env)
- def coerce_to_integer(self, env):
- # If not already some C integer type, coerce to longint.
- if self.arg.type.is_int:
- return self.arg
- else:
- return self.arg.coerce_to(PyrexTypes.c_long_type, env)
- def analyse_types(self, env):
- # The arg is always already analysed
- return self
- def generate_result_code(self, code):
- code.putln('%s; %s' % (
- self.arg.type.to_py_call_code(
- self.arg.result(),
- self.result(),
- self.target_type),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.py_result())
- class CoerceIntToBytesNode(CoerceToPyTypeNode):
- # This node is used to convert a C int type to a Python bytes
- # object.
- is_temp = 1
- def __init__(self, arg, env):
- arg = arg.coerce_to_simple(env)
- CoercionNode.__init__(self, arg)
- self.type = Builtin.bytes_type
- def generate_result_code(self, code):
- arg = self.arg
- arg_result = arg.result()
- if arg.type not in (PyrexTypes.c_char_type,
- PyrexTypes.c_uchar_type,
- PyrexTypes.c_schar_type):
- if arg.type.signed:
- code.putln("if ((%s < 0) || (%s > 255)) {" % (
- arg_result, arg_result))
- else:
- code.putln("if (%s > 255) {" % arg_result)
- code.putln('PyErr_SetString(PyExc_OverflowError, '
- '"value too large to pack into a byte"); %s' % (
- code.error_goto(self.pos)))
- code.putln('}')
- temp = None
- if arg.type is not PyrexTypes.c_char_type:
- temp = code.funcstate.allocate_temp(PyrexTypes.c_char_type, manage_ref=False)
- code.putln("%s = (char)%s;" % (temp, arg_result))
- arg_result = temp
- code.putln('%s = PyBytes_FromStringAndSize(&%s, 1); %s' % (
- self.result(),
- arg_result,
- code.error_goto_if_null(self.result(), self.pos)))
- if temp is not None:
- code.funcstate.release_temp(temp)
- code.put_gotref(self.py_result())
- class CoerceFromPyTypeNode(CoercionNode):
- # This node is used to convert a Python object
- # to a C data type.
- def __init__(self, result_type, arg, env):
- CoercionNode.__init__(self, arg)
- self.type = result_type
- self.is_temp = 1
- if not result_type.create_from_py_utility_code(env):
- error(arg.pos,
- "Cannot convert Python object to '%s'" % result_type)
- if self.type.is_string or self.type.is_pyunicode_ptr:
- if self.arg.is_name and self.arg.entry and self.arg.entry.is_pyglobal:
- warning(arg.pos,
- "Obtaining '%s' from externally modifiable global Python value" % result_type,
- level=1)
- def analyse_types(self, env):
- # The arg is always already analysed
- return self
- def is_ephemeral(self):
- return (self.type.is_ptr and not self.type.is_array) and self.arg.is_ephemeral()
- def generate_result_code(self, code):
- from_py_function = None
- # for certain source types, we can do better than the generic coercion
- if self.type.is_string and self.arg.type is bytes_type:
- if self.type.from_py_function.startswith('__Pyx_PyObject_As'):
- from_py_function = '__Pyx_PyBytes' + self.type.from_py_function[len('__Pyx_PyObject'):]
- NoneCheckNode.generate_if_needed(self.arg, code, "expected bytes, NoneType found")
- code.putln(self.type.from_py_call_code(
- self.arg.py_result(), self.result(), self.pos, code, from_py_function=from_py_function))
- if self.type.is_pyobject:
- code.put_gotref(self.py_result())
- def nogil_check(self, env):
- error(self.pos, "Coercion from Python not allowed without the GIL")
- class CoerceToBooleanNode(CoercionNode):
- # This node is used when a result needs to be used
- # in a boolean context.
- type = PyrexTypes.c_bint_type
- _special_builtins = {
- Builtin.list_type: 'PyList_GET_SIZE',
- Builtin.tuple_type: 'PyTuple_GET_SIZE',
- Builtin.set_type: 'PySet_GET_SIZE',
- Builtin.frozenset_type: 'PySet_GET_SIZE',
- Builtin.bytes_type: 'PyBytes_GET_SIZE',
- Builtin.bytearray_type: 'PyByteArray_GET_SIZE',
- Builtin.unicode_type: '__Pyx_PyUnicode_IS_TRUE',
- }
- def __init__(self, arg, env):
- CoercionNode.__init__(self, arg)
- if arg.type.is_pyobject:
- self.is_temp = 1
- def nogil_check(self, env):
- if self.arg.type.is_pyobject and self._special_builtins.get(self.arg.type) is None:
- self.gil_error()
- gil_message = "Truth-testing Python object"
- def check_const(self):
- if self.is_temp:
- self.not_const()
- return False
- return self.arg.check_const()
- def calculate_result_code(self):
- return "(%s != 0)" % self.arg.result()
- def generate_result_code(self, code):
- if not self.is_temp:
- return
- test_func = self._special_builtins.get(self.arg.type)
- if test_func is not None:
- checks = ["(%s != Py_None)" % self.arg.py_result()] if self.arg.may_be_none() else []
- checks.append("(%s(%s) != 0)" % (test_func, self.arg.py_result()))
- code.putln("%s = %s;" % (self.result(), '&&'.join(checks)))
- else:
- code.putln(
- "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
- self.result(),
- self.arg.py_result(),
- code.error_goto_if_neg(self.result(), self.pos)))
- class CoerceToComplexNode(CoercionNode):
- def __init__(self, arg, dst_type, env):
- if arg.type.is_complex:
- arg = arg.coerce_to_simple(env)
- self.type = dst_type
- CoercionNode.__init__(self, arg)
- dst_type.create_declaration_utility_code(env)
- def calculate_result_code(self):
- if self.arg.type.is_complex:
- real_part = "__Pyx_CREAL(%s)" % self.arg.result()
- imag_part = "__Pyx_CIMAG(%s)" % self.arg.result()
- else:
- real_part = self.arg.result()
- imag_part = "0"
- return "%s(%s, %s)" % (
- self.type.from_parts,
- real_part,
- imag_part)
- def generate_result_code(self, code):
- pass
- class CoerceToTempNode(CoercionNode):
- # This node is used to force the result of another node
- # to be stored in a temporary. It is only used if the
- # argument node's result is not already in a temporary.
- def __init__(self, arg, env):
- CoercionNode.__init__(self, arg)
- self.type = self.arg.type.as_argument_type()
- self.constant_result = self.arg.constant_result
- self.is_temp = 1
- if self.type.is_pyobject:
- self.result_ctype = py_object_type
- gil_message = "Creating temporary Python reference"
- def analyse_types(self, env):
- # The arg is always already analysed
- return self
- def coerce_to_boolean(self, env):
- self.arg = self.arg.coerce_to_boolean(env)
- if self.arg.is_simple():
- return self.arg
- self.type = self.arg.type
- self.result_ctype = self.type
- return self
- def generate_result_code(self, code):
- #self.arg.generate_evaluation_code(code) # Already done
- # by generic generate_subexpr_evaluation_code!
- code.putln("%s = %s;" % (
- self.result(), self.arg.result_as(self.ctype())))
- if self.use_managed_ref:
- if self.type.is_pyobject:
- code.put_incref(self.result(), self.ctype())
- elif self.type.is_memoryviewslice:
- code.put_incref_memoryviewslice(self.result(),
- not self.in_nogil_context)
- class ProxyNode(CoercionNode):
- """
- A node that should not be replaced by transforms or other means,
- and hence can be useful to wrap the argument to a clone node
- MyNode -> ProxyNode -> ArgNode
- CloneNode -^
- """
- nogil_check = None
- def __init__(self, arg):
- super(ProxyNode, self).__init__(arg)
- self.constant_result = arg.constant_result
- self._proxy_type()
- def analyse_types(self, env):
- self.arg = self.arg.analyse_expressions(env)
- self._proxy_type()
- return self
- def infer_type(self, env):
- return self.arg.infer_type(env)
- def _proxy_type(self):
- if hasattr(self.arg, 'type'):
- self.type = self.arg.type
- self.result_ctype = self.arg.result_ctype
- if hasattr(self.arg, 'entry'):
- self.entry = self.arg.entry
- def generate_result_code(self, code):
- self.arg.generate_result_code(code)
- def result(self):
- return self.arg.result()
- def is_simple(self):
- return self.arg.is_simple()
- def may_be_none(self):
- return self.arg.may_be_none()
- def generate_evaluation_code(self, code):
- self.arg.generate_evaluation_code(code)
- def generate_disposal_code(self, code):
- self.arg.generate_disposal_code(code)
- def free_temps(self, code):
- self.arg.free_temps(code)
- class CloneNode(CoercionNode):
- # This node is employed when the result of another node needs
- # to be used multiple times. The argument node's result must
- # be in a temporary. This node "borrows" the result from the
- # argument node, and does not generate any evaluation or
- # disposal code for it. The original owner of the argument
- # node is responsible for doing those things.
- subexprs = [] # Arg is not considered a subexpr
- nogil_check = None
- def __init__(self, arg):
- CoercionNode.__init__(self, arg)
- self.constant_result = arg.constant_result
- if hasattr(arg, 'type'):
- self.type = arg.type
- self.result_ctype = arg.result_ctype
- if hasattr(arg, 'entry'):
- self.entry = arg.entry
- def result(self):
- return self.arg.result()
- def may_be_none(self):
- return self.arg.may_be_none()
- def type_dependencies(self, env):
- return self.arg.type_dependencies(env)
- def infer_type(self, env):
- return self.arg.infer_type(env)
- def analyse_types(self, env):
- self.type = self.arg.type
- self.result_ctype = self.arg.result_ctype
- self.is_temp = 1
- if hasattr(self.arg, 'entry'):
- self.entry = self.arg.entry
- return self
- def coerce_to(self, dest_type, env):
- if self.arg.is_literal:
- return self.arg.coerce_to(dest_type, env)
- return super(CloneNode, self).coerce_to(dest_type, env)
- def is_simple(self):
- return True # result is always in a temp (or a name)
- def generate_evaluation_code(self, code):
- pass
- def generate_result_code(self, code):
- pass
- def generate_disposal_code(self, code):
- pass
- def free_temps(self, code):
- pass
- class CMethodSelfCloneNode(CloneNode):
- # Special CloneNode for the self argument of builtin C methods
- # that accepts subtypes of the builtin type. This is safe only
- # for 'final' subtypes, as subtypes of the declared type may
- # override the C method.
- def coerce_to(self, dst_type, env):
- if dst_type.is_builtin_type and self.type.subtype_of(dst_type):
- return self
- return CloneNode.coerce_to(self, dst_type, env)
- class ModuleRefNode(ExprNode):
- # Simple returns the module object
- type = py_object_type
- is_temp = False
- subexprs = []
- def analyse_types(self, env):
- return self
- def may_be_none(self):
- return False
- def calculate_result_code(self):
- return Naming.module_cname
- def generate_result_code(self, code):
- pass
- class DocstringRefNode(ExprNode):
- # Extracts the docstring of the body element
- subexprs = ['body']
- type = py_object_type
- is_temp = True
- def __init__(self, pos, body):
- ExprNode.__init__(self, pos)
- assert body.type.is_pyobject
- self.body = body
- def analyse_types(self, env):
- return self
- def generate_result_code(self, code):
- code.putln('%s = __Pyx_GetAttr(%s, %s); %s' % (
- self.result(), self.body.result(),
- code.intern_identifier(StringEncoding.EncodedString("__doc__")),
- code.error_goto_if_null(self.result(), self.pos)))
- code.put_gotref(self.result())
- #------------------------------------------------------------------------------------
- #
- # Runtime support code
- #
- #------------------------------------------------------------------------------------
- pyerr_occurred_withgil_utility_code= UtilityCode(
- proto = """
- static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void); /* proto */
- """,
- impl = """
- static CYTHON_INLINE int __Pyx_ErrOccurredWithGIL(void) {
- int err;
- #ifdef WITH_THREAD
- PyGILState_STATE _save = PyGILState_Ensure();
- #endif
- err = !!PyErr_Occurred();
- #ifdef WITH_THREAD
- PyGILState_Release(_save);
- #endif
- return err;
- }
- """
- )
- #------------------------------------------------------------------------------------
- raise_unbound_local_error_utility_code = UtilityCode(
- proto = """
- static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname);
- """,
- impl = """
- static CYTHON_INLINE void __Pyx_RaiseUnboundLocalError(const char *varname) {
- PyErr_Format(PyExc_UnboundLocalError, "local variable '%s' referenced before assignment", varname);
- }
- """)
- raise_closure_name_error_utility_code = UtilityCode(
- proto = """
- static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname);
- """,
- impl = """
- static CYTHON_INLINE void __Pyx_RaiseClosureNameError(const char *varname) {
- PyErr_Format(PyExc_NameError, "free variable '%s' referenced before assignment in enclosing scope", varname);
- }
- """)
- # Don't inline the function, it should really never be called in production
- raise_unbound_memoryview_utility_code_nogil = UtilityCode(
- proto = """
- static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname);
- """,
- impl = """
- static void __Pyx_RaiseUnboundMemoryviewSliceNogil(const char *varname) {
- #ifdef WITH_THREAD
- PyGILState_STATE gilstate = PyGILState_Ensure();
- #endif
- __Pyx_RaiseUnboundLocalError(varname);
- #ifdef WITH_THREAD
- PyGILState_Release(gilstate);
- #endif
- }
- """,
- requires = [raise_unbound_local_error_utility_code])
- #------------------------------------------------------------------------------------
- raise_too_many_values_to_unpack = UtilityCode.load_cached("RaiseTooManyValuesToUnpack", "ObjectHandling.c")
- raise_need_more_values_to_unpack = UtilityCode.load_cached("RaiseNeedMoreValuesToUnpack", "ObjectHandling.c")
- tuple_unpacking_error_code = UtilityCode.load_cached("UnpackTupleError", "ObjectHandling.c")
|